about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
committerMichael Biebl <biebl@debian.org>2024-05-05 00:07:30 +0200
commit34bb501be08aa2b313d88e67d6e0a7e0a3f9cfa6 (patch)
tree4e6220877828be4c6f261de09ec0cb2d80e32389 /src
parentbba2e4b4de668db525cbfdfc35292e5a0b51671a (diff)
New upstream version 1.47.90 upstream/1.47.90
Diffstat (limited to 'src')
-rw-r--r--src/README.md65
-rw-r--r--src/c-list/.editorconfig11
-rw-r--r--src/c-list/.github/workflows/ci.yml32
-rw-r--r--src/c-list/AUTHORS41
-rw-r--r--src/c-list/NEWS.md77
-rw-r--r--src/c-list/README.md52
-rw-r--r--src/c-list/meson.build46
-rw-r--r--src/c-list/src/meson.build34
-rw-r--r--src/c-list/src/test-api.c144
-rw-r--r--src/c-list/src/test-basic.c319
-rw-r--r--src/c-list/src/test-embed.c173
-rw-r--r--src/c-rbtree/.editorconfig11
-rw-r--r--src/c-rbtree/.github/workflows/ci.yml41
-rw-r--r--src/c-rbtree/.gitmodules0
-rw-r--r--src/c-rbtree/.readthedocs.yaml23
-rw-r--r--src/c-rbtree/AUTHORS40
-rw-r--r--src/c-rbtree/NEWS.md53
-rw-r--r--src/c-rbtree/README.md55
-rw-r--r--src/c-rbtree/meson.build22
-rw-r--r--src/c-rbtree/meson_options.txt1
-rw-r--r--src/c-rbtree/src/docs/api.rst5
-rw-r--r--src/c-rbtree/src/docs/conf.py45
-rw-r--r--src/c-rbtree/src/docs/index.rst15
-rw-r--r--src/c-rbtree/src/docs/requirements.txt5
-rw-r--r--src/c-rbtree/src/libcrbtree.sym21
-rw-r--r--src/c-rbtree/src/meson.build70
-rw-r--r--src/c-rbtree/src/test-api.c107
-rw-r--r--src/c-rbtree/src/test-basic.c239
-rw-r--r--src/c-rbtree/src/test-map.c277
-rw-r--r--src/c-rbtree/src/test-misc.c66
-rw-r--r--src/c-rbtree/src/test-parallel.c381
-rw-r--r--src/c-rbtree/src/test-posix.c270
l---------src/c-rbtree/subprojects/libcstdaux-11
-rw-r--r--src/c-siphash/.editorconfig11
-rw-r--r--src/c-siphash/.github/workflows/ci.yml32
-rw-r--r--src/c-siphash/.gitmodules0
-rw-r--r--src/c-siphash/AUTHORS38
-rw-r--r--src/c-siphash/NEWS.md9
-rw-r--r--src/c-siphash/README.md53
-rw-r--r--src/c-siphash/meson.build21
-rw-r--r--src/c-siphash/src/libcsiphash.def9
-rw-r--r--src/c-siphash/src/libcsiphash.sym16
-rw-r--r--src/c-siphash/src/meson.build58
-rw-r--r--src/c-siphash/src/test-api.c31
-rw-r--r--src/c-siphash/src/test-basic.c120
l---------src/c-siphash/subprojects/libcstdaux-11
-rw-r--r--src/c-stdaux/.editorconfig11
-rw-r--r--src/c-stdaux/.github/workflows/ci.yml37
-rw-r--r--src/c-stdaux/.readthedocs.yaml20
-rw-r--r--src/c-stdaux/AUTHORS43
-rw-r--r--src/c-stdaux/NEWS.md106
-rw-r--r--src/c-stdaux/README.md54
-rw-r--r--src/c-stdaux/meson.build110
-rw-r--r--src/c-stdaux/meson_options.txt7
-rw-r--r--src/c-stdaux/src/docs/api.rst5
-rw-r--r--src/c-stdaux/src/docs/conf.py45
-rw-r--r--src/c-stdaux/src/docs/index.rst14
-rw-r--r--src/c-stdaux/src/docs/requirements.txt3
-rw-r--r--src/c-stdaux/src/libcstdaux.sym6
-rw-r--r--src/c-stdaux/src/meson.build43
-rw-r--r--src/c-stdaux/src/test-api.c322
-rw-r--r--src/c-stdaux/src/test-basic.c624
-rw-r--r--src/contrib/README.md11
-rw-r--r--src/core/NetworkManagerUtils.c4
-rw-r--r--src/core/README.l3cfg.md368
-rw-r--r--src/core/README.md9
-rw-r--r--src/core/README.next.ip-config.md59
-rw-r--r--src/core/devices/nm-device-bond.c4
-rw-r--r--src/core/devices/nm-device-bridge.c4
-rw-r--r--src/core/devices/nm-device-ethernet.c12
-rw-r--r--src/core/devices/nm-device-factory.c3
-rw-r--r--src/core/devices/nm-device-utils.c16
-rw-r--r--src/core/devices/nm-device-vrf.c2
-rw-r--r--src/core/devices/nm-device.c541
-rw-r--r--src/core/devices/nm-device.h15
-rw-r--r--src/core/devices/ovs/nm-device-ovs-bridge.c2
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c4
-rw-r--r--src/core/devices/ovs/nm-device-ovs-port.c4
-rw-r--r--src/core/devices/team/nm-device-team.c4
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c4
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c6
-rw-r--r--src/core/devices/wifi/nm-wifi-ap.c15
-rw-r--r--src/core/devices/wwan/meson.build3
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c64
-rw-r--r--src/core/dhcp/README.next.md103
-rw-r--r--src/core/dhcp/nm-dhcp-client.c7
-rw-r--r--src/core/dhcp/nm-dhcp-nettools.c5
-rw-r--r--src/core/dhcp/nm-dhcp-systemd.c56
-rw-r--r--src/core/meson.build2
-rw-r--r--src/core/ndisc/nm-ndisc.c1
-rw-r--r--src/core/ndisc/nm-ndisc.h2
-rw-r--r--src/core/nm-active-connection.c133
-rw-r--r--src/core/nm-active-connection.h10
-rw-r--r--src/core/nm-audit-manager.c17
-rw-r--r--src/core/nm-checkpoint.c270
-rw-r--r--src/core/nm-config-data.c47
-rw-r--r--src/core/nm-config-data.h3
-rw-r--r--src/core/nm-connectivity.h8
-rw-r--r--src/core/nm-core-utils.c3
-rw-r--r--src/core/nm-core-utils.h2
-rw-r--r--src/core/nm-l3cfg.c2
-rw-r--r--src/core/nm-manager.c171
-rw-r--r--src/core/nm-manager.h3
-rw-r--r--src/core/nm-policy.c2
-rw-r--r--src/core/nm-power-monitor.c (renamed from src/core/nm-sleep-monitor.c)162
-rw-r--r--src/core/nm-power-monitor.h34
-rw-r--r--src/core/nm-sleep-monitor.h33
-rw-r--r--src/core/nm-types.h4
-rw-r--r--src/core/settings/nm-settings-connection.c23
-rw-r--r--src/core/settings/nm-settings-connection.h4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c14
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c1
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c8
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv21
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c18
-rw-r--r--src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c4
-rw-r--r--src/core/supplicant/nm-supplicant-config.c3
-rw-r--r--src/core/supplicant/nm-supplicant-settings-verify.c1
-rw-r--r--src/core/tests/config/test-config.c2
-rw-r--r--src/core/tests/test-dcb.c2
-rw-r--r--src/core/vpn/nm-vpn-connection.c116
-rw-r--r--src/libnm-base/README.md20
-rw-r--r--src/libnm-base/nm-base.h4
-rw-r--r--src/libnm-client-impl/README.md14
-rw-r--r--src/libnm-client-impl/libnm.ver25
-rw-r--r--src/libnm-client-impl/meson.build13
-rw-r--r--src/libnm-client-impl/nm-conn-utils.c4
-rw-r--r--src/libnm-client-impl/nm-device-ethernet.c14
-rw-r--r--src/libnm-client-impl/tests/meson.build4
-rw-r--r--src/libnm-client-impl/tests/test-libnm.c8
-rw-r--r--src/libnm-client-impl/tests/test-nm-client.c2
-rw-r--r--src/libnm-client-public/README.md16
-rw-r--r--src/libnm-client-test/README.md11
-rw-r--r--src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in34
-rw-r--r--src/libnm-core-impl/nm-connection.c4
-rw-r--r--src/libnm-core-impl/nm-setting-6lowpan.c2
-rw-r--r--src/libnm-core-impl/nm-setting-8021x.c117
-rw-r--r--src/libnm-core-impl/nm-setting-connection.c61
-rw-r--r--src/libnm-core-impl/nm-setting-ip-config.c45
-rw-r--r--src/libnm-core-impl/nm-setting-ip6-config.c100
-rw-r--r--src/libnm-core-impl/nm-setting-loopback.c2
-rw-r--r--src/libnm-core-impl/nm-setting-macsec.c2
-rw-r--r--src/libnm-core-impl/nm-setting-match.c4
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-bridge.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-interface.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-port.c2
-rw-r--r--src/libnm-core-impl/nm-setting-private.h82
-rw-r--r--src/libnm-core-impl/nm-setting-team.c8
-rw-r--r--src/libnm-core-impl/nm-setting-vlan.c2
-rw-r--r--src/libnm-core-impl/nm-setting-wired.c461
-rw-r--r--src/libnm-core-impl/nm-setting-wireless.c471
-rw-r--r--src/libnm-core-impl/nm-setting.c31
-rw-r--r--src/libnm-core-impl/nm-team-utils.c58
-rw-r--r--src/libnm-core-impl/nm-utils.c3
-rw-r--r--src/libnm-core-impl/tests/test-general.c5
-rw-r--r--src/libnm-core-impl/tests/test-keyfile.c2
-rw-r--r--src/libnm-core-impl/tests/test-secrets.c2
-rw-r--r--src/libnm-core-impl/tests/test-setting.c6
-rw-r--r--src/libnm-core-intern/nm-core-internal.h8
-rw-r--r--src/libnm-core-public/nm-dbus-interface.h34
-rw-r--r--src/libnm-core-public/nm-dbus-types.xml2361
-rw-r--r--src/libnm-core-public/nm-setting-8021x.h3
-rw-r--r--src/libnm-core-public/nm-setting-connection.h22
-rw-r--r--src/libnm-core-public/nm-setting-ip-config.h3
-rw-r--r--src/libnm-core-public/nm-setting-ip6-config.h8
-rw-r--r--src/libnm-core-public/nm-setting-wired.h25
-rw-r--r--src/libnm-core-public/nm-setting-wireless.h29
-rw-r--r--src/libnm-core-public/nm-version-macros.h98
-rw-r--r--src/libnm-core-public/nm-version-macros.h.in1
-rw-r--r--src/libnm-core-public/nm-version.h14
-rw-r--r--src/libnm-core-public/nm-vpn-dbus-types.xml246
-rw-r--r--src/libnm-crypto/nm-crypto.c10
-rw-r--r--src/libnm-glib-aux/README.md16
-rw-r--r--src/libnm-glib-aux/nm-macros-internal.h6
-rw-r--r--src/libnm-glib-aux/nm-uuid.c9
-rw-r--r--src/libnm-lldp/nm-lldp-neighbor.c3
-rw-r--r--src/libnm-lldp/nm-lldp-network.c2
-rw-r--r--src/libnm-log-core/README.md10
-rw-r--r--src/libnm-log-null/README.md10
-rw-r--r--src/libnm-platform/README.md22
-rw-r--r--src/libnm-platform/nm-linux-platform.c69
-rw-r--r--src/libnm-platform/nm-netlink.c9
-rw-r--r--src/libnm-platform/nm-platform.c17
-rw-r--r--src/libnm-platform/nmp-object.c22
-rw-r--r--src/libnm-platform/wifi/nm-wifi-utils-nl80211.c94
-rw-r--r--src/libnm-std-aux/README.md16
-rw-r--r--src/libnm-std-aux/nm-linux-compat.h2
-rw-r--r--src/libnm-systemd-core/README.md23
-rw-r--r--src/libnm-systemd-core/meson.build6
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/netif-util.c221
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/netif-util.h22
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c9
-rw-r--r--src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h83
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c252
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h75
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h10
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h12
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h21
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c62
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/network-common.c111
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/network-common.h19
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c290
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c217
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c355
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h117
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c962
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h77
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h74
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c2724
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c32
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h6
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c22
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c71
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h16
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c22
-rw-r--r--src/libnm-systemd-core/src/systemd/_sd-common.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-device.h1
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h70
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h168
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h52
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h174
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-id128.h1
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ndisc.h73
-rw-r--r--src/libnm-systemd-shared/README.md57
-rw-r--r--src/libnm-systemd-shared/meson.build3
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h (renamed from src/libnm-systemd-shared/sd-adapt-shared/netif-util.h)0
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h53
-rw-r--r--src/libnm-systemd-shared/src/basic/arphrd-util.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/async.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.c100
-rw-r--r--src/libnm-systemd-shared/src/basic/btrfs.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h101
-rw-r--r--src/libnm-systemd-shared/src/basic/chase.h64
-rw-r--r--src/libnm-systemd-shared/src/basic/constants.h23
-rw-r--r--src/libnm-systemd-shared/src/basic/devnum-util.c142
-rw-r--r--src/libnm-systemd-shared/src/basic/devnum-util.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.c39
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c400
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.h36
-rw-r--r--src/libnm-systemd-shared/src/basic/errno-util.h142
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c31
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.c41
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.h5
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c266
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h37
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c272
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h15
-rw-r--r--src/libnm-systemd-shared/src/basic/format-util.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c55
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.h28
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.c10
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c95
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h31
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c54
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h28
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.c37
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.h26
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.c158
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.h76
-rw-r--r--src/libnm-systemd-shared/src/basic/iovec-util.h99
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/lock-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.h51
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_fcntl.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_socket.h25
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_stat.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h52
-rw-r--r--src/libnm-systemd-shared/src/basic/namespace-util.h57
-rw-r--r--src/libnm-systemd-shared/src/basic/ordered-set.c5
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c74
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c96
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/pidref.h78
-rw-r--r--src/libnm-systemd-shared/src/basic/prioq.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c695
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h116
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/ratelimit.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c86
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/siphash24.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c255
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h43
-rw-r--r--src/libnm-systemd-shared/src/basic/sort-util.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c272
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h40
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c324
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h55
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c139
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h30
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c126
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/umask-util.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/user-util.h23
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.c33
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.h2
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h152
-rw-r--r--src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h42
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.c11
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c16
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h8
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.c43
-rw-r--r--src/libnm-udev-aux/README.md9
-rw-r--r--src/libnmc-base/README.md12
-rw-r--r--src/libnmc-base/nm-client-utils.c29
-rw-r--r--src/libnmc-setting/README.md17
-rw-r--r--src/libnmc-setting/meson.build8
-rw-r--r--src/libnmc-setting/nm-meta-setting-desc.c88
-rw-r--r--src/libnmc-setting/settings-docs.h469
-rw-r--r--src/libnmc-setting/settings-docs.h.in30
-rw-r--r--src/libnmt-newt/README.md4
-rw-r--r--src/linux-headers/README.md15
-rw-r--r--src/meson.build8
-rw-r--r--src/n-acd/.editorconfig11
-rw-r--r--src/n-acd/.github/workflows/ci.yml122
-rw-r--r--src/n-acd/.gitmodules12
-rw-r--r--src/n-acd/AUTHORS39
-rw-r--r--src/n-acd/NEWS.md46
-rw-r--r--src/n-acd/README.md60
-rw-r--r--src/n-acd/meson.build27
-rw-r--r--src/n-acd/meson_options.txt1
-rw-r--r--src/n-acd/src/libnacd.sym28
-rw-r--r--src/n-acd/src/meson.build95
-rw-r--r--src/n-acd/src/test-api.c88
-rw-r--r--src/n-acd/src/test-bpf.c226
-rw-r--r--src/n-acd/src/test-loopback.c82
-rw-r--r--src/n-acd/src/test-twice.c97
-rw-r--r--src/n-acd/src/test-unplug.c84
-rw-r--r--src/n-acd/src/test-unused.c63
-rw-r--r--src/n-acd/src/test-veth.c240
-rw-r--r--src/n-acd/src/test.h213
-rw-r--r--src/n-acd/src/util/test-timer.c177
l---------src/n-acd/subprojects/c-list1
l---------src/n-acd/subprojects/c-rbtree1
l---------src/n-acd/subprojects/c-siphash1
l---------src/n-acd/subprojects/libcstdaux-11
-rw-r--r--src/n-dhcp4/.editorconfig11
-rw-r--r--src/n-dhcp4/.github/workflows/ci.yml50
-rw-r--r--src/n-dhcp4/.gitmodules9
-rw-r--r--src/n-dhcp4/AUTHORS37
-rw-r--r--src/n-dhcp4/NEWS.md20
-rw-r--r--src/n-dhcp4/README.md53
-rw-r--r--src/n-dhcp4/meson.build23
-rw-r--r--src/n-dhcp4/src/libndhcp4.sym71
-rw-r--r--src/n-dhcp4/src/meson.build90
-rw-r--r--src/n-dhcp4/src/n-dhcp4-c-connection.c4
-rw-r--r--src/n-dhcp4/src/n-dhcp4-c-probe.c27
-rw-r--r--src/n-dhcp4/src/n-dhcp4-s-connection.c412
-rw-r--r--src/n-dhcp4/src/n-dhcp4-s-lease.c103
-rw-r--r--src/n-dhcp4/src/n-dhcp4-server.c241
-rw-r--r--src/n-dhcp4/src/n-dhcp4.h1
-rw-r--r--src/n-dhcp4/src/test-api.c157
-rw-r--r--src/n-dhcp4/src/test-connection.c390
-rw-r--r--src/n-dhcp4/src/test-message.c159
-rw-r--r--src/n-dhcp4/src/test-run-client.c713
-rw-r--r--src/n-dhcp4/src/test-socket.c317
-rw-r--r--src/n-dhcp4/src/test.h107
-rw-r--r--src/n-dhcp4/src/util/link.c287
-rw-r--r--src/n-dhcp4/src/util/link.h38
-rw-r--r--src/n-dhcp4/src/util/netns.c165
-rw-r--r--src/n-dhcp4/src/util/netns.h27
-rw-r--r--src/n-dhcp4/src/util/test-packet.c411
l---------src/n-dhcp4/subprojects/c-list1
l---------src/n-dhcp4/subprojects/c-siphash1
-rw-r--r--src/nm-cloud-setup/README.md8
-rw-r--r--src/nm-compat-headers/README.md10
-rw-r--r--src/nm-daemon-helper/README.md11
-rw-r--r--src/nm-dispatcher/README.md15
-rw-r--r--src/nm-dispatcher/nm-dispatcher.c3
-rw-r--r--src/nm-initrd-generator/README.md12
-rw-r--r--src/nm-initrd-generator/nmi-cmdline-reader.c2
-rw-r--r--src/nm-initrd-generator/tests/test-cmdline-reader.c22
-rw-r--r--src/nm-online/README.md14
-rw-r--r--src/nm-priv-helper/README.md24
-rw-r--r--src/nmcli/README.md13
-rw-r--r--src/nmcli/common.h1
-rw-r--r--src/nmcli/gen-metadata-nm-settings-nmcli.xml.in51
-rw-r--r--src/nmcli/general.c11
-rw-r--r--src/nmcli/settings.c2
-rw-r--r--src/nmcli/utils.h1
-rw-r--r--src/nmtui/README.md15
-rw-r--r--src/nmtui/nmt-8021x-fields.c171
-rw-r--r--src/nmtui/nmt-connect-connection-list.c2
-rw-r--r--src/nmtui/nmt-port-list.c2
-rw-r--r--src/nmtui/nmtui-edit.c4
-rw-r--r--src/tests/README.md8
-rw-r--r--src/tests/client/meson.build4
-rw-r--r--src/tests/client/terminal-colors.d/nmcli.enable0
-rw-r--r--src/tests/client/terminal-colors.d/nmcli.schem0
-rw-r--r--src/tests/client/test-client.check-on-disk/test_002.expected10
-rw-r--r--src/tests/client/test-client.check-on-disk/test_003.expected1194
-rw-r--r--src/tests/client/test-client.check-on-disk/test_004.expected1288
-rwxr-xr-xsrc/tests/client/test-client.py12
416 files changed, 26027 insertions, 7480 deletions
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 00000000..59c062f6
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,65 @@
+src/
+====
+
+Most of the subdirectories are static helper libraries, which
+get linked into one of the final build artifacts (like libnm,
+nmcli or NetworkManager). Static libraries are internal API.
+
+The only public API is libnm, which is a shared library provided
+client implementations.
+
+Our own clients (like nmcli and nmtui) also use libnm, the shared library.
+But they also use additional static helper libraries.
+
+The daemon statically links against a part of libnm, the part that provides
+connection profiles. That is libnm-core. libnm-core is thus statically linked
+with libnm and the daemon. It does not get linked by clients that already link
+with libnm (like nmtui).
+
+Read the individual README.md files in the subdirectories for details:
+
+| Directory                                            | Description                                             |
+|------------------------------------------------------|---------------------------------------------------------|
+| [core/](core/)                                       | the NetworkManager daemon |
+| [nmcli/](nmcli/)                                     | nmcli application, a command line client for NetworkManager |
+| [nmtui/](nmtui/)                                     | nmtui application, a text UI client for NetworkManager |
+| [nm-cloud-setup/](nm-cloud-setup/)                   | service to automatically configure NetworkManager in cloud environment |
+| [nm-initrd-generator/](nm-initrd-generator/)         | generates NetworkManager configuration by parsing kernel command line options for dracut/initrd |
+| [nm-dispatcher/](nm-dispatcher/)                     | NetworkManager-dispatcher service to run user scripts |
+| [nm-online/](nm-online/)                             | application which checks whether NetworkManager is done, for implementing NetworkManager-wait-online.service |
+| [nm-priv-helper/](nm-priv-helper/)                   | internal service for privileged operations |
+| [nm-daemon-helper/](nm-daemon-helper/)               | internal helper binary spawned by NetworkManager |
+|                                                      | |
+| [libnm-std-aux/](libnm-std-aux/)                     | internal helper library for standard C |
+| [libnm-glib-aux/](libnm-glib-aux/)                   | internal helper library for glib |
+| [libnm-log-null/](libnm-log-null/)                   | internal helper library with dummy (null) logging backend |
+| [libnm-log-core/](libnm-log-core/)                   | internal helper library with logging backend (syslog) used by daemon |
+| [libnm-base/](libnm-base/)                           | internal helper library with base definitions |
+| [libnm-platform/](libnm-platform/)                   | internal helper library for netlink and other platform/kernel API |
+| [libnm-udev-aux/](libnm-udev-aux/)                   | internal helper library for libudev |
+|                                                      | |
+| [libnm-core-public/](libnm-core-public/)             | public API of libnm (libnm-core part) |
+| [libnm-core-intern/](libnm-core-intern/)             | internal API of libnm-core, used by libnm and daemon |
+| [libnm-core-impl/](libnm-core-impl/)                 | implementation of libnm-core |
+| [libnm-core-aux-intern/](libnm-core-aux-intern/)     | internal helper library on top of libnm-core (used by libnm-core itself) |
+| [libnm-core-aux-extern/](libnm-core-aux-extern/)     | internal helper library on top of libnm-core (not used by libnm-core) |
+| [libnm-client-public/](libnm-client-public/)         | public API of libnm (NMClient part) |
+| [libnm-client-impl/](libnm-client-impl/)             | implementation of libnm (NMClient) |
+| [libnm-client-aux-extern/](libnm-client-aux-extern/) | internal helper library on top of libnm (not used by libnm itself) |
+| [libnmc-base/](libnmc-base/)                         | internal helper library for libnm clients |
+| [libnmc-setting/](libnmc-setting/)                   | internal helper library for setting connection profiles (used by nmcli) |
+| [libnmt-newt/](libnmt-newt/)                         | internal helper library for libnewt for nmtui |
+|                                                      | |
+| [linux-headers/](linux-headers/)                     | extra Linux kernel UAPI headers |
+| [contrib/](contrib/)                                 | sources that are not used by NetworkManager itself |
+| [tests/](tests/)                                     | unit tests that are not specific to one of the other directories |
+| [libnm-client-test/](libnm-client-test/)             | internal helper library with test utils for libnm |
+|                                                      | |
+| [c-list/](c-list/)                                   | fork of c-util helper library for intrusive, doubly linked list |
+| [c-rbtree/](c-rbtree/)                               | fork of c-util helper library for intrusive Red-Black Tree |
+| [c-siphash/](c-siphash/)                             | fork of c-util helper library for SIPHash24 |
+| [c-stdaux/](c-stdaux/)                               | fork of c-util general purpose helpers for standard C |
+| [n-acd/](n-acd/)                                     | fork of nettools IPv4 ACD library |
+| [n-dhcp4/](n-dhcp4/)                                 | fork of nettools DHCPv4 library |
+| [libnm-systemd-core/](libnm-systemd-core/)           | fork of systemd code as network library |
+| [libnm-systemd-shared/](libnm-systemd-shared/)       | fork of systemd code as general purpose library |
diff --git a/src/c-list/.editorconfig b/src/c-list/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/c-list/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 8
diff --git a/src/c-list/.github/workflows/ci.yml b/src/c-list/.github/workflows/ci.yml
new file mode 100644
index 00000000..00bca960
--- /dev/null
+++ b/src/c-list/.github/workflows/ci.yml
@@ -0,0 +1,32 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  ci-linux:
+    name: Linux CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: true
+      m32: true
+      matrixmode: true
+      valgrind: true
+  ci-macos:
+    name: MacOS CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      macos: true
+  ci-windows:
+    name: Windows CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      windows: true
diff --git a/src/c-list/AUTHORS b/src/c-list/AUTHORS
new file mode 100644
index 00000000..76dea872
--- /dev/null
+++ b/src/c-list/AUTHORS
@@ -0,0 +1,41 @@
+LICENSE:
+        This project is dual-licensed under both the Apache License, Version
+        2.0, and the GNU Lesser General Public License, Version 2.1+.
+
+AUTHORS-ASL:
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+                http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+
+AUTHORS-LGPL:
+        This program is free software; you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published
+        by the Free Software Foundation; either version 2.1 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public License
+        along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+COPYRIGHT: (ordered alphabetically)
+        Copyright (C) 2015-2022 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        Danilo Horta <danilo.horta@pm.me>
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Lucas De Marchi <lucas.de.marchi@gmail.com>
+        Michele Dionisio
+        Thomas Haller <thaller@redhat.com>
+        Tom Gundersen <teg@jklm.no>
diff --git a/src/c-list/NEWS.md b/src/c-list/NEWS.md
new file mode 100644
index 00000000..095f1ea7
--- /dev/null
+++ b/src/c-list/NEWS.md
@@ -0,0 +1,77 @@
+# c-list - Circular Intrusive Double Linked List Collection
+
+## CHANGES WITH 3.1.0:
+
+        * The minimum required meson version is now 0.60.0.
+
+        * New function c_list_split() is added. It reverses c_list_splice()
+          and thus allows to split a list in half.
+
+        Contributions from: David Rheinsberg, Michele Dionisio
+
+        - Brno, 2022-06-22
+
+## CHANGES WITH 3:
+
+        * API break: The c_list_loop_*() symbols were removed, since we saw
+                     little use for them. No user was known at the time, so
+                     all projects should build with the new API version
+                     unchanged.
+                     Since c-list does not distribute any compiled code, there
+                     is no ABI issue with this change.
+
+        * Two new symbols c_list_length() and c_list_contains(). They are meant
+          for debugging purposes, to easily verify list integrity. Since they
+          run in O(n) time, they are not recommended for any other use than
+          debugging.
+
+        * New symbol c_list_init() is provided as alternative to the verbose
+          C_LIST_INIT assignment.
+
+        * The c-list API is extended to work well with `const CList` objects.
+          That is, any read-only accessor function allows constant objects as
+          input now.
+          Note that this does not propagate into other members linked in the
+          list. Using `const` for CList members is of little practical use.
+          However, it might be of use for its embedding objects, so we now
+          allow it in the CList API as well.
+
+        * The c_list_splice() call now clears the source list, rather than
+          returning with stale pointers. Technically, this is also an API
+          break, but unlikely to affect any existing code.
+
+        Contributions from: David Herrmann, Thomas Haller
+
+        - Berlin, 2017-08-13
+
+## CHANGES WITH 2:
+
+        * Adjust project-name in build-system to reflect the actual project. The
+          previous releases incorrectly claimed to be c-rbtree in the build
+          system.
+
+        * Add c_list_swap() that swaps two lists given their head pointers.
+
+        * Add c_list_splice() that moves a list.
+
+        * Add LGPL2.1+ as license so c-list can be imported into GPL2 projects.
+          It is now officially dual-licensed.
+
+        * As usual a bunch of fixes, additional tests, and documentation
+          updates.
+
+        Contributions from: David Herrmann, Tom Gundersen
+
+        - Lund, 2017-05-03
+
+## CHANGES WITH 1:
+
+        * Initial release of c-list.
+
+        * This project provides an implementation of a circular double linked
+          list in standard ISO-C11. License is ASL-2.0 and the build system
+          used is `Meson'.
+
+        Contributions from: David Herrmann, Tom Gundersen
+
+        - Berlin, 2017-03-03
diff --git a/src/c-list/README.md b/src/c-list/README.md
new file mode 100644
index 00000000..a2e47528
--- /dev/null
+++ b/src/c-list/README.md
@@ -0,0 +1,52 @@
+c-list
+======
+
+Circular Intrusive Double Linked List Collection
+
+The c-list project implements an intrusive collection based on circular double
+linked lists in ISO-C11. It aims for minimal API constraints, leaving maximum
+control over the data-structures to the API consumer.
+
+### Project
+
+ * **Website**: <https://c-util.github.io/c-list>
+ * **Bug Tracker**: <https://github.com/c-util/c-list/issues>
+
+### Requirements
+
+The requirements for this project are:
+
+ * `libc` (e.g., `glibc >= 2.16`)
+
+At build-time, the following software is required:
+
+ * `meson >= 0.60`
+ * `pkg-config >= 0.29`
+
+### Build
+
+The meson build-system is used for this project. Contact upstream
+documentation for detailed help. In most situations the following
+commands are sufficient to build and install from source:
+
+```sh
+mkdir build
+cd build
+meson setup ..
+ninja
+meson test
+ninja install
+```
+
+No custom configuration options are available.
+
+### Repository:
+
+ - **web**:   <https://github.com/c-util/c-list>
+ - **https**: `https://github.com/c-util/c-list.git`
+ - **ssh**:   `git@github.com:c-util/c-list.git`
+
+### License:
+
+ - **Apache-2.0** OR **LGPL-2.1-or-later**
+ - See AUTHORS file for details.
diff --git a/src/c-list/meson.build b/src/c-list/meson.build
new file mode 100644
index 00000000..b5ef78e6
--- /dev/null
+++ b/src/c-list/meson.build
@@ -0,0 +1,46 @@
+project(
+        'c-list',
+        'c',
+        default_options: [
+                'c_std=c99',
+        ],
+        license: 'Apache',
+        meson_version: '>=0.60.0',
+        version: '3.1.0',
+)
+major = meson.project_version().split('.')[0]
+project_description = 'Circular Intrusive Double Linked List Collection'
+
+mod_pkgconfig = import('pkgconfig')
+
+# See c-stdaux for details on these. We do not have c-stdaux as dependency, so
+# we keep a duplicated set here, reduced to the minimum.
+cflags = meson.get_compiler('c').get_supported_arguments(
+        '-D_GNU_SOURCE',
+
+        '-Wno-gnu-alignof-expression',
+        '-Wno-maybe-uninitialized',
+        '-Wno-unknown-warning-option',
+        '-Wno-unused-parameter',
+
+        '-Wno-error=type-limits',
+        '-Wno-error=missing-field-initializers',
+
+        '-Wdate-time',
+        '-Wdeclaration-after-statement',
+        '-Wlogical-op',
+        '-Wmissing-include-dirs',
+        '-Wmissing-noreturn',
+        '-Wnested-externs',
+        '-Wredundant-decls',
+        '-Wshadow',
+        '-Wstrict-aliasing=3',
+        '-Wsuggest-attribute=noreturn',
+        '-Wundef',
+        '-Wwrite-strings',
+)
+add_project_arguments(cflags, language: 'c')
+
+subdir('src')
+
+meson.override_dependency('libclist-'+major, libclist_dep, static: true)
diff --git a/src/c-list/src/meson.build b/src/c-list/src/meson.build
new file mode 100644
index 00000000..ec7f29d5
--- /dev/null
+++ b/src/c-list/src/meson.build
@@ -0,0 +1,34 @@
+#
+# target: libclist.so
+# (No .so is built so far, since we are header-only. This might change in the
+#  future, if we add more complex list helpers.)
+#
+
+libclist_dep = declare_dependency(
+        include_directories: include_directories('.'),
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers('c-list.h')
+
+        mod_pkgconfig.generate(
+                description: project_description,
+                filebase: 'libclist-'+major,
+                name: 'libclist',
+                version: meson.project_version(),
+        )
+endif
+
+#
+# target: test-*
+#
+
+test_api = executable('test-api', ['test-api.c'], dependencies: libclist_dep)
+test('API Symbol Visibility', test_api)
+
+test_basic = executable('test-basic', ['test-basic.c'], dependencies: libclist_dep)
+test('Basic API Behavior', test_basic)
+
+test_embed = executable('test-embed', ['test-embed.c'], dependencies: libclist_dep)
+test('Embedded List Nodes', test_embed)
diff --git a/src/c-list/src/test-api.c b/src/c-list/src/test-api.c
new file mode 100644
index 00000000..864d198a
--- /dev/null
+++ b/src/c-list/src/test-api.c
@@ -0,0 +1,144 @@
+/*
+ * Tests for Public API
+ * This test, unlikely the others, is linked against the real, distributed,
+ * shared library. Its sole purpose is to test for symbol availability.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-list.h"
+
+typedef struct {
+        int id;
+        CList link;
+} Node;
+
+static void test_api(void) {
+        CList *list_iter, *list_safe;
+        CList list = C_LIST_INIT(list), list2 = C_LIST_INIT(list2);
+        Node node = { .id = 0, .link = C_LIST_INIT(node.link) };
+
+        assert(c_list_init(&list) == &list);
+        assert(!c_list_entry_offset(NULL, 0));
+        assert(!c_list_entry_offset(NULL, offsetof(Node, link)));
+        assert(!c_list_entry(NULL, Node, link));
+        assert(c_list_entry(&node.link, Node, link) == &node);
+        assert(!c_list_is_linked(&node.link));
+        assert(c_list_is_empty(&list));
+        assert(c_list_length(&list) == 0);
+        assert(c_list_contains(&list, &list));
+        assert(!c_list_contains(&list, &node.link));
+        c_list_flush(&list);
+
+        /* basic link / unlink calls */
+
+        c_list_link_before(&list, &node.link);
+        assert(c_list_is_linked(&node.link));
+        assert(!c_list_is_empty(&list));
+        assert(c_list_length(&list) == 1);
+        assert(c_list_contains(&list, &list));
+        assert(c_list_contains(&list, &node.link));
+
+        c_list_unlink_stale(&node.link);
+        assert(c_list_is_linked(&node.link));
+        assert(c_list_is_empty(&list));
+        assert(c_list_length(&list) == 0);
+
+        c_list_link_after(&list, &node.link);
+        assert(c_list_is_linked(&node.link));
+        assert(!c_list_is_empty(&list));
+
+        c_list_unlink(&node.link);
+        assert(!c_list_is_linked(&node.link));
+        assert(c_list_is_empty(&list));
+
+        /* link / unlink aliases */
+
+        c_list_link_front(&list, &node.link);
+        assert(c_list_is_linked(&node.link));
+
+        c_list_unlink(&node.link);
+        assert(!c_list_is_linked(&node.link));
+
+        c_list_link_tail(&list, &node.link);
+        assert(c_list_is_linked(&node.link));
+
+        c_list_unlink(&node.link);
+        assert(!c_list_is_linked(&node.link));
+
+        /* swap / splice / split list operators */
+
+        c_list_swap(&list, &list);
+        assert(c_list_is_empty(&list));
+
+        c_list_splice(&list, &list);
+        assert(c_list_is_empty(&list));
+
+        c_list_split(&list, &list, &list2);
+        assert(c_list_is_empty(&list));
+        assert(c_list_is_empty(&list2));
+
+        /* direct/raw iterators */
+
+        c_list_for_each(list_iter, &list)
+                assert(list_iter != &list);
+
+        c_list_for_each_safe(list_iter, list_safe, &list)
+                assert(list_iter != &list);
+
+        list_iter = NULL;
+        c_list_for_each_continue(list_iter, &list)
+                assert(list_iter != &list);
+
+        list_iter = NULL;
+        c_list_for_each_safe_continue(list_iter, list_safe, &list)
+                assert(list_iter != &list);
+
+        c_list_for_each_safe_unlink(list_iter, list_safe, &list)
+                assert(list_iter != &list);
+
+        /* list accessors */
+
+        assert(!c_list_first(&list));
+        assert(!c_list_last(&list));
+        assert(!c_list_first_entry(&list, Node, link));
+        assert(!c_list_last_entry(&list, Node, link));
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+static void test_api_gnu(void) {
+        CList list = C_LIST_INIT(list);
+        Node *node_iter, *node_safe;
+
+        /* c_list_entry() based iterators */
+
+        c_list_for_each_entry(node_iter, &list, link)
+                assert(&node_iter->link != &list);
+
+        c_list_for_each_entry_safe(node_iter, node_safe, &list, link)
+                assert(&node_iter->link != &list);
+
+        node_iter = NULL;
+        c_list_for_each_entry_continue(node_iter, &list, link)
+                assert(&node_iter->link != &list);
+
+        node_iter = NULL;
+        c_list_for_each_entry_safe_continue(node_iter, node_safe, &list, link)
+                assert(&node_iter->link != &list);
+
+        c_list_for_each_entry_safe_unlink(node_iter, node_safe, &list, link)
+                assert(&node_iter->link != &list);
+}
+#else
+static void test_api_gnu(void) {
+}
+#endif
+
+int main(void) {
+        test_api();
+        test_api_gnu();
+        return 0;
+}
diff --git a/src/c-list/src/test-basic.c b/src/c-list/src/test-basic.c
new file mode 100644
index 00000000..58ed8636
--- /dev/null
+++ b/src/c-list/src/test-basic.c
@@ -0,0 +1,319 @@
+/*
+ * Tests for basic functionality
+ * This contains basic, deterministic tests for list behavior, API
+ * functionality, and usage.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-list.h"
+
+static void assert_list_integrity(CList *list) {
+        CList *iter;
+
+        iter = list;
+        do {
+                assert(iter->next->prev == iter);
+                assert(iter->prev->next == iter);
+
+                iter = iter->next;
+        } while (iter != list);
+}
+
+static void test_iterators(void) {
+        CList *iter, *safe, a, b, list = C_LIST_INIT(list);
+        unsigned int i;
+
+        assert(!c_list_first(&list));
+        assert(!c_list_last(&list));
+
+        /* link @a and verify iterators see just it */
+
+        c_list_link_tail(&list, &a);
+        assert(c_list_is_linked(&a));
+        assert(c_list_first(&list) == &a);
+        assert(c_list_last(&list) == &a);
+
+        i = 0;
+        c_list_for_each(iter, &list) {
+                assert(iter == &a);
+                ++i;
+        }
+        assert(i == 1);
+
+        i = 0;
+        iter = NULL;
+        c_list_for_each_continue(iter, &list) {
+                assert(iter == &a);
+                ++i;
+        }
+        assert(i == 1);
+
+        i = 0;
+        iter = &a;
+        c_list_for_each_continue(iter, &list)
+                ++i;
+        assert(i == 0);
+
+        /* link @b as well and verify iterators again */
+
+        c_list_link_tail(&list, &b);
+        assert(c_list_is_linked(&a));
+        assert(c_list_is_linked(&b));
+
+        i = 0;
+        c_list_for_each(iter, &list) {
+                assert((i == 0 && iter == &a) ||
+                       (i == 1 && iter == &b));
+                ++i;
+        }
+        assert(i == 2);
+
+        i = 0;
+        iter = NULL;
+        c_list_for_each_continue(iter, &list) {
+                assert((i == 0 && iter == &a) ||
+                       (i == 1 && iter == &b));
+                ++i;
+        }
+        assert(i == 2);
+
+        i = 0;
+        iter = &a;
+        c_list_for_each_continue(iter, &list) {
+                assert(iter == &b);
+                ++i;
+        }
+        assert(i == 1);
+
+        i = 0;
+        iter = &b;
+        c_list_for_each_continue(iter, &list)
+                ++i;
+        assert(i == 0);
+
+        /* verify safe-iterator while removing elements */
+
+        i = 0;
+        c_list_for_each_safe(iter, safe, &list) {
+                assert(iter == &a || iter == &b);
+                c_list_unlink_stale(iter);
+                ++i;
+        }
+        assert(i == 2);
+
+        assert(c_list_is_empty(&list));
+
+        /* link both and verify *_unlink() iterators */
+
+        c_list_link_tail(&list, &a);
+        c_list_link_tail(&list, &b);
+
+        i = 0;
+        c_list_for_each_safe_unlink(iter, safe, &list) {
+                assert(iter == &a || iter == &b);
+                assert(!c_list_is_linked(iter));
+                ++i;
+        }
+        assert(i == 2);
+
+        assert(c_list_is_empty(&list));
+}
+
+static void test_swap(void) {
+        CList list1 = (CList)C_LIST_INIT(list1);
+        CList list2 = (CList)C_LIST_INIT(list2);
+        CList list;
+
+        c_list_swap(&list1, &list2);
+
+        assert(list1.prev == list1.next && list1.prev == &list1);
+        assert(list2.prev == list2.next && list2.prev == &list2);
+
+        c_list_link_tail(&list1, &list);
+
+        assert(c_list_first(&list1) == &list);
+        assert(c_list_last(&list1) == &list);
+        assert(list.next == &list1);
+        assert(list.prev == &list1);
+
+        c_list_swap(&list1, &list2);
+
+        assert(c_list_first(&list2) == &list);
+        assert(c_list_last(&list2) == &list);
+        assert(list.next == &list2);
+        assert(list.prev == &list2);
+
+        assert(list1.prev == list1.next && list1.prev == &list1);
+}
+
+static void test_splice(void) {
+        CList target = (CList)C_LIST_INIT(target);
+        CList source = (CList)C_LIST_INIT(source);
+        CList e1, e2;
+
+        c_list_link_tail(&source, &e1);
+
+        c_list_splice(&target, &source);
+        assert(c_list_first(&target) == &e1);
+        assert(c_list_last(&target) == &e1);
+
+        source = (CList)C_LIST_INIT(source);
+
+        c_list_link_tail(&source, &e2);
+
+        c_list_splice(&target, &source);
+        assert(c_list_first(&target) == &e1);
+        assert(c_list_last(&target) == &e2);
+}
+
+static void test_split(void) {
+        CList e1, e2;
+
+        /* split empty list */
+        {
+                CList source = C_LIST_INIT(source), target;
+
+                c_list_split(&source, &source, &target);
+                assert(c_list_is_empty(&source));
+                assert(c_list_is_empty(&target));
+                assert_list_integrity(&source);
+                assert_list_integrity(&target);
+        }
+
+        /* split 1-element list excluding the element */
+        {
+                CList source = C_LIST_INIT(source), target;
+
+                c_list_link_tail(&source, &e1);
+                c_list_split(&source, &source, &target);
+                assert(!c_list_is_empty(&source));
+                assert(c_list_is_empty(&target));
+                assert_list_integrity(&source);
+                assert_list_integrity(&target);
+        }
+
+        /* split 1-element list including the element */
+        {
+                CList source = C_LIST_INIT(source), target;
+
+                c_list_link_tail(&source, &e1);
+                c_list_split(&source, &e1, &target);
+                assert(c_list_is_empty(&source));
+                assert(!c_list_is_empty(&target));
+                assert_list_integrity(&source);
+                assert_list_integrity(&target);
+        }
+
+        /* split 2-element list excluding the elements */
+        {
+                CList source = C_LIST_INIT(source), target;
+
+                c_list_link_tail(&source, &e1);
+                c_list_link_tail(&source, &e2);
+                c_list_split(&source, &source, &target);
+                assert(!c_list_is_empty(&source));
+                assert(c_list_is_empty(&target));
+                assert_list_integrity(&source);
+                assert_list_integrity(&target);
+        }
+
+        /* split 2-element list including one element */
+        {
+                CList source = C_LIST_INIT(source), target;
+
+                c_list_link_tail(&source, &e1);
+                c_list_link_tail(&source, &e2);
+                c_list_split(&source, &e2, &target);
+                assert(!c_list_is_empty(&source));
+                assert(!c_list_is_empty(&target));
+                assert_list_integrity(&source);
+                assert_list_integrity(&target);
+        }
+
+        /* split 2-element list including both elements */
+        {
+                CList source = C_LIST_INIT(source), target;
+
+                c_list_link_tail(&source, &e1);
+                c_list_link_tail(&source, &e2);
+                c_list_split(&source, &e1, &target);
+                assert(c_list_is_empty(&source));
+                assert(!c_list_is_empty(&target));
+                assert_list_integrity(&source);
+                assert_list_integrity(&target);
+        }
+}
+
+
+static void test_flush(void) {
+        CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2);
+        CList list1 = C_LIST_INIT(list1), list2 = C_LIST_INIT(list2);
+
+        c_list_link_tail(&list2, &e1);
+        c_list_link_tail(&list2, &e2);
+
+        assert(c_list_is_linked(&e1));
+        assert(c_list_is_linked(&e2));
+
+        c_list_flush(&list1);
+        c_list_flush(&list2);
+
+        assert(!c_list_is_linked(&e1));
+        assert(!c_list_is_linked(&e2));
+}
+
+static void test_macros(void) {
+        /* Verify `c_list_entry()` evaluates arguments only once. */
+        {
+                struct TestList {
+                        int a;
+                        CList link;
+                        int b;
+                } list = { .link = C_LIST_INIT(list.link) };
+                CList *p[2] = { &list.link, NULL };
+                unsigned int i = 0;
+
+                assert(i == 0);
+                assert(c_list_entry(p[i++], struct TestList, link) == &list);
+                assert(i == 1);
+        }
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+static void test_gnu(void) {
+        CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2);
+
+        /* Test `c_list_flush()` in combination with cleanup attributes. */
+        {
+                __attribute((cleanup(c_list_flush))) CList list1 = C_LIST_INIT(list1);
+                __attribute((cleanup(c_list_flush))) CList list2 = C_LIST_INIT(list2);
+
+                c_list_link_tail(&list2, &e1);
+                c_list_link_tail(&list2, &e2);
+
+                assert(c_list_is_linked(&e1));
+                assert(c_list_is_linked(&e2));
+        }
+
+        assert(!c_list_is_linked(&e1));
+        assert(!c_list_is_linked(&e2));
+}
+#else
+static void test_gnu(void) {
+}
+#endif
+
+int main(void) {
+        test_iterators();
+        test_swap();
+        test_splice();
+        test_split();
+        test_flush();
+        test_macros();
+        test_gnu();
+        return 0;
+}
diff --git a/src/c-list/src/test-embed.c b/src/c-list/src/test-embed.c
new file mode 100644
index 00000000..7ee6ff01
--- /dev/null
+++ b/src/c-list/src/test-embed.c
@@ -0,0 +1,173 @@
+/*
+ * Tests for embedded CList members
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-list.h"
+
+typedef struct Entry Entry;
+
+struct Entry {
+        short foo;
+        CList link;
+        short bar;
+};
+
+static void test_entry(void) {
+        CList list = C_LIST_INIT(list);
+        Entry e1 = { .foo = 1 * 7, .bar = 1 * 11 };
+        Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 };
+        Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 };
+        Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 };
+        Entry *e;
+        CList *iter, *safe;
+        size_t i;
+
+        /* verify c_list_entry() works as expected (even with NULL) */
+
+        assert(!c_list_entry(NULL, Entry, link));
+        assert(&e1 == c_list_entry(&e1.link, Entry, link));
+
+        /* verify @list is empty */
+
+        assert(!c_list_first_entry(&list, Entry, link));
+        assert(!c_list_last_entry(&list, Entry, link));
+
+        /* link 2 entries and verify list state */
+
+        c_list_link_tail(&list, &e1.link);
+        c_list_link_tail(&list, &e2.link);
+
+        assert(c_list_first_entry(&list, Entry, link)->foo == 1 * 7);
+        assert(c_list_first_entry(&list, Entry, link)->bar == 1 * 11);
+        assert(c_list_last_entry(&list, Entry, link)->foo == 2 * 7);
+        assert(c_list_last_entry(&list, Entry, link)->bar == 2 * 11);
+
+        i = 0;
+        c_list_for_each(iter, &list) {
+                e = c_list_entry(iter, Entry, link);
+                assert(i != 0 || e == &e1);
+                assert(i != 1 || e == &e2);
+                assert(i < 2);
+                ++i;
+        }
+        assert(i == 2);
+
+        /* link 2 more entries */
+
+        c_list_link_tail(&list, &e3.link);
+        c_list_link_tail(&list, &e4.link);
+
+        assert(c_list_first_entry(&list, Entry, link)->foo == 1 * 7);
+        assert(c_list_first_entry(&list, Entry, link)->bar == 1 * 11);
+        assert(c_list_last_entry(&list, Entry, link)->foo == 4 * 7);
+        assert(c_list_last_entry(&list, Entry, link)->bar == 4 * 11);
+
+        i = 0;
+        c_list_for_each(iter, &list) {
+                e = c_list_entry(iter, Entry, link);
+                assert(i != 0 || e == &e1);
+                assert(i != 1 || e == &e2);
+                assert(i != 2 || e == &e3);
+                assert(i != 3 || e == &e4);
+                assert(i < 4);
+                ++i;
+        }
+        assert(i == 4);
+
+        assert(!c_list_is_empty(&list));
+        assert(c_list_is_linked(&e1.link));
+        assert(c_list_is_linked(&e2.link));
+        assert(c_list_is_linked(&e3.link));
+        assert(c_list_is_linked(&e4.link));
+
+        /* remove via safe iterator */
+
+        i = 0;
+        c_list_for_each_safe(iter, safe, &list) {
+                e = c_list_entry(iter, Entry, link);
+                assert(i != 0 || e == &e1);
+                assert(i != 1 || e == &e2);
+                assert(i != 2 || e == &e3);
+                assert(i != 3 || e == &e4);
+                assert(i < 4);
+                ++i;
+                c_list_unlink(&e->link);
+        }
+        assert(i == 4);
+
+        assert(c_list_is_empty(&list));
+        assert(!c_list_is_linked(&e1.link));
+        assert(!c_list_is_linked(&e2.link));
+        assert(!c_list_is_linked(&e3.link));
+        assert(!c_list_is_linked(&e4.link));
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+static void test_entry_gnu(void) {
+        CList list = C_LIST_INIT(list);
+        Entry e1 = { .foo = 1 * 7, .bar = 1 * 11 };
+        Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 };
+        Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 };
+        Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 };
+        Entry *e, *safe;
+        size_t i;
+
+        /* link entries and verify list state */
+
+        c_list_link_tail(&list, &e1.link);
+        c_list_link_tail(&list, &e2.link);
+        c_list_link_tail(&list, &e3.link);
+        c_list_link_tail(&list, &e4.link);
+
+        i = 0;
+        c_list_for_each_entry(e, &list, link) {
+                assert(i != 0 || e == &e1);
+                assert(i != 1 || e == &e2);
+                assert(i != 2 || e == &e3);
+                assert(i != 3 || e == &e4);
+                assert(i < 4);
+                ++i;
+        }
+        assert(i == 4);
+
+        assert(!c_list_is_empty(&list));
+        assert(c_list_is_linked(&e1.link));
+        assert(c_list_is_linked(&e2.link));
+        assert(c_list_is_linked(&e3.link));
+        assert(c_list_is_linked(&e4.link));
+
+        /* remove via safe iterator */
+
+        i = 0;
+        c_list_for_each_entry_safe(e, safe, &list, link) {
+                assert(i != 0 || e == &e1);
+                assert(i != 1 || e == &e2);
+                assert(i != 2 || e == &e3);
+                assert(i != 3 || e == &e4);
+                assert(i < 4);
+                ++i;
+                c_list_unlink(&e->link);
+        }
+        assert(i == 4);
+
+        assert(c_list_is_empty(&list));
+        assert(!c_list_is_linked(&e1.link));
+        assert(!c_list_is_linked(&e2.link));
+        assert(!c_list_is_linked(&e3.link));
+        assert(!c_list_is_linked(&e4.link));
+}
+#else
+static void test_entry_gnu(void) {
+}
+#endif
+
+int main(void) {
+        test_entry();
+        test_entry_gnu();
+        return 0;
+}
diff --git a/src/c-rbtree/.editorconfig b/src/c-rbtree/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/c-rbtree/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 8
diff --git a/src/c-rbtree/.github/workflows/ci.yml b/src/c-rbtree/.github/workflows/ci.yml
new file mode 100644
index 00000000..489433c2
--- /dev/null
+++ b/src/c-rbtree/.github/workflows/ci.yml
@@ -0,0 +1,41 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  ci-linux:
+    name: Linux CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: true
+      m32: true
+      matrixmode: true
+      valgrind: true
+
+  ci-linux-ptrace:
+    name: Linux CI with PTrace
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: true
+      mesonargs: '-Dptrace=true'
+
+  ci-macos:
+    name: MacOS CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      macos: true
+
+  ci-docs:
+    name: Documentation CI
+    uses: bus1/cabuild/.github/workflows/ci-sphinx.yml@v1
+    with:
+      meson: true
+      source: "./src/docs"
diff --git a/src/c-rbtree/.gitmodules b/src/c-rbtree/.gitmodules
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/c-rbtree/.gitmodules
diff --git a/src/c-rbtree/.readthedocs.yaml b/src/c-rbtree/.readthedocs.yaml
new file mode 100644
index 00000000..fd02d297
--- /dev/null
+++ b/src/c-rbtree/.readthedocs.yaml
@@ -0,0 +1,23 @@
+# Read the Docs configuration file
+
+version: 2
+
+build:
+  apt_packages:
+  - "clang"
+  jobs:
+    pre_build:
+    - meson subprojects download
+  os: "ubuntu-22.04"
+  tools:
+    python: "3"
+
+formats: "all"
+
+python:
+  install:
+  - requirements: "src/docs/requirements.txt"
+  system_packages: true
+
+sphinx:
+  configuration: "src/docs/conf.py"
diff --git a/src/c-rbtree/AUTHORS b/src/c-rbtree/AUTHORS
new file mode 100644
index 00000000..7e4f368d
--- /dev/null
+++ b/src/c-rbtree/AUTHORS
@@ -0,0 +1,40 @@
+LICENSE:
+        This project is dual-licensed under both the Apache License, Version
+        2.0, and the GNU Lesser General Public License, Version 2.1+.
+
+AUTHORS-ASL:
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+                http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+
+AUTHORS-LGPL:
+        This program is free software; you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published
+        by the Free Software Foundation; either version 2.1 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public License
+        along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+COPYRIGHT: (ordered alphabetically)
+        Copyright (C) 2015-2022 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Evgeny Vereshchagin <evvers@ya.ru>
+        Kay Sievers <kay@vrfy.org>
+        Thomas Haller <thaller@redhat.com>
+        Tom Gundersen <teg@jklm.no>
diff --git a/src/c-rbtree/NEWS.md b/src/c-rbtree/NEWS.md
new file mode 100644
index 00000000..71415f67
--- /dev/null
+++ b/src/c-rbtree/NEWS.md
@@ -0,0 +1,53 @@
+# c-rbtree - Intrusive Red-Black Tree Collection
+
+## CHANGES WITH 3.1.0:
+
+        * Add 'ptrace' build option to enable running tests using 'ptrace'
+          to verify extended execution properties. This option should not
+          be used in setups where 'ptrace' cannot be employed (like running
+          under gdb or valgrind). This option only affects the test-suite.
+
+        * meson-0.60.0 is now the minimum required meson version.
+
+        Contributions from: David Rheinsberg, Evgeny Vereshchagin
+
+        - Brno, 2022-06-22
+
+## CHANGES WITH 3:
+
+        * Add more helpers. Add both a collection of iteratiors and helpers
+          for initializing a tree and checking if a tree is empty, without
+          explicitly accessing the data structure.
+
+        Contributions from: David Herrmann
+
+        - Berlin, 2017-08-13
+
+## CHANGES WITH 2:
+
+        * Relicense as ASL-2.0 to make c-rbtree useful for more projects. All
+          code is now fully available under the ASL-2.0. Nothing is covered by
+          the LGPL, anymore.
+
+        * Switch build-system from Autotools to Meson. This simplifies the code
+          base significantly. The Meson Build System is now used by many other
+          projects, including GStreamer, Weston, and several Gnome packages.
+          See http://mesonbuild.com/ for more information.
+
+        Contributions from: David Herrmann
+
+        - Berlin, 2016-12-14
+
+## CHANGES WITH 1:
+
+        * Initial release of c-rbtree.
+
+        * This projects provides an RB-Tree API, that is fully implemented in
+          ISO-C11 and has no external dependencies. Furthermore, tree
+          traversal, memory allocations, and key comparisons are completely
+          controlled by the API user. The implementation only provides the
+          RB-Tree specific rebalancing and coloring.
+
+        Contributions from: David Herrmann, Kay Sievers, Tom Gundersen
+
+        - Berlin, 2016-08-31
diff --git a/src/c-rbtree/README.md b/src/c-rbtree/README.md
new file mode 100644
index 00000000..ff3e1cb7
--- /dev/null
+++ b/src/c-rbtree/README.md
@@ -0,0 +1,55 @@
+c-rbtree
+========
+
+Intrusive Red-Black Tree Collection
+
+The c-rbtree project implements an intrusive collection based on red-black
+trees in ISO-C11. Its API guarantees the user full control over its
+data-structures, and rather limits itself to just the tree-specific rebalancing
+and coloring operations. For API documentation, see the c-rbtree.h header file,
+as well as the docbook comments for each function.
+
+### Project
+
+ * **Website**: <https://c-util.github.io/c-rbtree>
+ * **Documentation**: <https://c-rbtree.readthedocs.io>
+ * **Bug Tracker**: <https://github.com/c-util/c-rbtree/issues>
+
+### Requirements
+
+The requirements for this project are:
+
+ * `libc` (e.g., `glibc >= 2.16`)
+
+At build-time, the following software is required:
+
+ * `meson >= 0.60`
+ * `pkg-config >= 0.29`
+
+### Build
+
+The meson build-system is used for this project. Contact upstream
+documentation for detailed help. In most situations the following
+commands are sufficient to build and install from source:
+
+```sh
+mkdir build
+cd build
+meson setup ..
+ninja
+meson test
+ninja install
+```
+
+No custom configuration options are available.
+
+### Repository:
+
+ - **web**:   <https://github.com/c-util/c-rbtree>
+ - **https**: `https://github.com/c-util/c-rbtree.git`
+ - **ssh**:   `git@github.com:c-util/c-rbtree.git`
+
+### License:
+
+ - **Apache-2.0** OR **LGPL-2.1-or-later**
+ - See AUTHORS file for details.
diff --git a/src/c-rbtree/meson.build b/src/c-rbtree/meson.build
new file mode 100644
index 00000000..3e7af9ce
--- /dev/null
+++ b/src/c-rbtree/meson.build
@@ -0,0 +1,22 @@
+project(
+        'c-rbtree',
+        'c',
+        default_options: [
+                'c_std=c11'
+        ],
+        license: 'Apache',
+        meson_version: '>=0.60.0',
+        version: '3.1.0',
+)
+major = meson.project_version().split('.')[0]
+project_description = 'Intrusive Red-Black Tree Collection'
+
+mod_pkgconfig = import('pkgconfig')
+use_ptrace = get_option('ptrace')
+
+dep_cstdaux = dependency('libcstdaux-1')
+add_project_arguments(dep_cstdaux.get_variable('cflags').split(' '), language: 'c')
+
+subdir('src')
+
+meson.override_dependency('libcrbtree-'+major, libcrbtree_dep, static: true)
diff --git a/src/c-rbtree/meson_options.txt b/src/c-rbtree/meson_options.txt
new file mode 100644
index 00000000..ec358180
--- /dev/null
+++ b/src/c-rbtree/meson_options.txt
@@ -0,0 +1 @@
+option('ptrace', type: 'boolean', value: false, description: 'Allow ptrace in test suite')
diff --git a/src/c-rbtree/src/docs/api.rst b/src/c-rbtree/src/docs/api.rst
new file mode 100644
index 00000000..fb41b9e8
--- /dev/null
+++ b/src/c-rbtree/src/docs/api.rst
@@ -0,0 +1,5 @@
+API
+===
+
+.. c:autodoc:: c-*.h c-*.c
+   :transform: kerneldoc
diff --git a/src/c-rbtree/src/docs/conf.py b/src/c-rbtree/src/docs/conf.py
new file mode 100644
index 00000000..f804486c
--- /dev/null
+++ b/src/c-rbtree/src/docs/conf.py
@@ -0,0 +1,45 @@
+#
+# Sphinx Documentation Configuration
+#
+
+import re
+import os
+import sys
+
+import capidocs.kerneldoc
+import hawkmoth
+
+# Global Setup
+
+project = 'c-rbtree'
+
+author = 'C-Util Community'
+copyright = '2015-2022, C-Util Community'
+
+# Hawkmoth C-Audodoc Setup
+
+capidocs.kerneldoc.hawkmoth_conf()
+
+# Extensions
+
+exclude_patterns = []
+
+extensions = [
+    'hawkmoth',
+]
+
+# Hawkmoth Options
+
+cautodoc_clang = capidocs.kerneldoc.hawkmoth_include_args()
+cautodoc_clang += ["-I" + os.path.abspath("..")]
+cautodoc_clang += capidocs.kerneldoc.hawkmoth_glob_includes("../../subprojects", "libc*/src")
+
+cautodoc_root = os.path.abspath('..')
+
+cautodoc_transformations = {
+    'kerneldoc': capidocs.kerneldoc.hawkmoth_converter,
+}
+
+# HTML Options
+
+html_theme = 'sphinx_rtd_theme'
diff --git a/src/c-rbtree/src/docs/index.rst b/src/c-rbtree/src/docs/index.rst
new file mode 100644
index 00000000..df281b0b
--- /dev/null
+++ b/src/c-rbtree/src/docs/index.rst
@@ -0,0 +1,15 @@
+Introduction
+============
+
+The **c-rbtree** project provides a Red-Black-Tree API, that is fully
+implemented in ISO-C11 and has no external dependencies. Furthermore, tree
+traversal, memory allocations, and key comparisons are completely controlled by
+the API user. The implementation only provides the RB-Tree specific rebalancing
+and coloring.
+
+.. toctree::
+   :caption: Library Documentation
+   :hidden:
+
+   self
+   api
diff --git a/src/c-rbtree/src/docs/requirements.txt b/src/c-rbtree/src/docs/requirements.txt
new file mode 100644
index 00000000..77973ebd
--- /dev/null
+++ b/src/c-rbtree/src/docs/requirements.txt
@@ -0,0 +1,5 @@
+c-apidocs>=0.0.3
+clang>=6
+hawkmoth>=0.7
+meson>=0.60
+ninja>=1.10
diff --git a/src/c-rbtree/src/libcrbtree.sym b/src/c-rbtree/src/libcrbtree.sym
new file mode 100644
index 00000000..e7b801b8
--- /dev/null
+++ b/src/c-rbtree/src/libcrbtree.sym
@@ -0,0 +1,21 @@
+LIBCRBTREE_3 {
+global:
+        c_rbnode_leftmost;
+        c_rbnode_rightmost;
+        c_rbnode_leftdeepest;
+        c_rbnode_rightdeepest;
+        c_rbnode_next;
+        c_rbnode_prev;
+        c_rbnode_next_postorder;
+        c_rbnode_prev_postorder;
+        c_rbnode_link;
+        c_rbnode_unlink_stale;
+        c_rbtree_first;
+        c_rbtree_last;
+        c_rbtree_first_postorder;
+        c_rbtree_last_postorder;
+        c_rbtree_add;
+        c_rbtree_move;
+local:
+       *;
+};
diff --git a/src/c-rbtree/src/meson.build b/src/c-rbtree/src/meson.build
new file mode 100644
index 00000000..94612031
--- /dev/null
+++ b/src/c-rbtree/src/meson.build
@@ -0,0 +1,70 @@
+#
+# target: libcrbtree.so
+#
+
+libcrbtree_symfile = join_paths(meson.current_source_dir(), 'libcrbtree.sym')
+
+libcrbtree_deps = [
+        dep_cstdaux,
+]
+
+libcrbtree_both = both_libraries(
+        'crbtree-'+major,
+        [
+                'c-rbtree.c',
+        ],
+        c_args: [
+                '-fvisibility=hidden',
+                '-fno-common',
+        ],
+        dependencies: libcrbtree_deps,
+        install: not meson.is_subproject(),
+        link_args: dep_cstdaux.get_variable('version-scripts') == 'yes' ? [
+                '-Wl,--version-script=@0@'.format(libcrbtree_symfile),
+        ] : [],
+        link_depends: libcrbtree_symfile,
+        soversion: 0,
+)
+
+libcrbtree_dep = declare_dependency(
+        dependencies: libcrbtree_deps,
+        include_directories: include_directories('.'),
+        link_with: libcrbtree_both.get_static_lib(),
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers('c-rbtree.h')
+
+        mod_pkgconfig.generate(
+                description: project_description,
+                filebase: 'libcrbtree-'+major,
+                libraries: libcrbtree_both.get_shared_lib(),
+                name: 'libcrbtree',
+                version: meson.project_version(),
+        )
+endif
+
+#
+# target: test-*
+#
+
+test_api = executable('test-api', ['test-api.c'], link_with: libcrbtree_both.get_shared_lib())
+test('API Symbol Visibility', test_api)
+
+test_basic = executable('test-basic', ['test-basic.c'], dependencies: libcrbtree_dep)
+test('Basic API Behavior', test_basic)
+
+test_map = executable('test-map', ['test-map.c'], dependencies: libcrbtree_dep)
+test('Generic Map', test_map)
+
+test_misc = executable('test-misc', ['test-misc.c'], dependencies: libcrbtree_dep)
+test('Miscellaneous', test_misc)
+
+if use_ptrace
+        test_parallel = executable('test-parallel', ['test-parallel.c'], dependencies: libcrbtree_dep)
+        test('Lockless Parallel Readers', test_parallel)
+
+        test_posix = executable('test-posix', ['test-posix.c'], dependencies: libcrbtree_dep)
+        test('Posix tsearch(3p) Comparison', test_posix)
+endif
diff --git a/src/c-rbtree/src/test-api.c b/src/c-rbtree/src/test-api.c
new file mode 100644
index 00000000..295f6b9b
--- /dev/null
+++ b/src/c-rbtree/src/test-api.c
@@ -0,0 +1,107 @@
+/*
+ * Tests for Public API
+ * This test, unlikely the others, is linked against the real, distributed,
+ * shared library. Its sole purpose is to test for symbol availability.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-rbtree.h"
+
+typedef struct TestNode {
+        CRBNode rb;
+} TestNode;
+
+static void test_api(void) {
+        CRBTree t = C_RBTREE_INIT, t2 = C_RBTREE_INIT;
+        CRBNode *i, *is, n = C_RBNODE_INIT(n), m = C_RBNODE_INIT(m);
+        TestNode *ie, *ies;
+
+        assert(c_rbtree_is_empty(&t));
+        assert(!c_rbnode_is_linked(&n));
+        assert(!c_rbnode_entry(NULL, TestNode, rb));
+
+        /* init, is_linked, add, link, {unlink{,_stale}} */
+
+        c_rbtree_add(&t, NULL, &t.root, &n);
+        assert(c_rbnode_is_linked(&n));
+
+        c_rbnode_link(&n, &n.left, &m);
+        assert(c_rbnode_is_linked(&m));
+
+        c_rbnode_unlink(&m);
+        assert(!c_rbnode_is_linked(&m));
+
+        c_rbtree_add(&t, NULL, &t.root, &n);
+        assert(c_rbnode_is_linked(&n));
+
+        c_rbnode_link(&n, &n.left, &m);
+        assert(c_rbnode_is_linked(&m));
+
+        c_rbnode_unlink_stale(&m);
+        assert(c_rbnode_is_linked(&m)); /* @m wasn't touched */
+
+        c_rbnode_init(&n);
+        assert(!c_rbnode_is_linked(&n));
+
+        c_rbnode_init(&m);
+        assert(!c_rbnode_is_linked(&m));
+
+        c_rbtree_init(&t);
+        assert(c_rbtree_is_empty(&t));
+
+        /* move */
+
+        c_rbtree_move(&t2, &t);
+
+        /* first, last, leftmost, rightmost, next, prev */
+
+        assert(!c_rbtree_first(&t));
+        assert(!c_rbtree_last(&t));
+        assert(&n == c_rbnode_leftmost(&n));
+        assert(&n == c_rbnode_rightmost(&n));
+        assert(!c_rbnode_next(&n));
+        assert(!c_rbnode_prev(&n));
+
+        /* postorder traversal */
+
+        assert(!c_rbtree_first_postorder(&t));
+        assert(!c_rbtree_last_postorder(&t));
+        assert(&n == c_rbnode_leftdeepest(&n));
+        assert(&n == c_rbnode_rightdeepest(&n));
+        assert(!c_rbnode_next_postorder(&n));
+        assert(!c_rbnode_prev_postorder(&n));
+
+        /* iterators */
+
+        c_rbtree_for_each(i, &t)
+                assert(!i);
+        c_rbtree_for_each_safe(i, is, &t)
+                assert(!i);
+        c_rbtree_for_each_entry(ie, &t, rb)
+                assert(!ie);
+        c_rbtree_for_each_entry_safe(ie, ies, &t, rb)
+                assert(!ie);
+
+        c_rbtree_for_each_postorder(i, &t)
+                assert(!i);
+        c_rbtree_for_each_safe_postorder(i, is, &t)
+                assert(!i);
+        c_rbtree_for_each_entry_postorder(ie, &t, rb)
+                assert(!ie);
+        c_rbtree_for_each_entry_safe_postorder(ie, ies, &t, rb)
+                assert(!ie);
+
+        c_rbtree_for_each_safe_postorder_unlink(i, is, &t)
+                assert(!i);
+        c_rbtree_for_each_entry_safe_postorder_unlink(ie, ies, &t, rb)
+                assert(!ie);
+}
+
+int main(int argc, char **argv) {
+        test_api();
+        return 0;
+}
diff --git a/src/c-rbtree/src/test-basic.c b/src/c-rbtree/src/test-basic.c
new file mode 100644
index 00000000..8fee6468
--- /dev/null
+++ b/src/c-rbtree/src/test-basic.c
@@ -0,0 +1,239 @@
+/*
+ * Tests for Basic Tree Operations
+ * This test does some basic tree operations and verifies their correctness. It
+ * validates the RB-Tree invariants after each operation, to guarantee the
+ * stability of the tree.
+ *
+ * For testing purposes, we use the memory address of a node as its key, and
+ * order nodes in ascending order.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "c-rbtree.h"
+#include "c-rbtree-private.h"
+
+static size_t validate(CRBTree *t) {
+        unsigned int i_black, n_black;
+        CRBNode *n, *p, *o;
+        size_t count = 0;
+
+        c_assert(t);
+        c_assert(!t->root || c_rbnode_is_black(t->root));
+
+        /* traverse to left-most child, count black nodes */
+        i_black = 0;
+        n = t->root;
+        while (n && n->left) {
+                if (c_rbnode_is_black(n))
+                        ++i_black;
+                n = n->left;
+        }
+        n_black = i_black;
+
+        /*
+         * Traverse tree and verify correctness:
+         *  1) A node is either red or black
+         *  2) The root is black
+         *  3) All leaves are black
+         *  4) Every red node must have two black child nodes
+         *  5) Every path to a leaf contains the same number of black nodes
+         *
+         * Note that NULL nodes are considered black, which is why we don't
+         * check for 3).
+         */
+        o = NULL;
+        while (n) {
+                ++count;
+
+                /* verify natural order */
+                c_assert(n > o);
+                o = n;
+
+                /* verify consistency */
+                c_assert(!n->right || c_rbnode_parent(n->right) == n);
+                c_assert(!n->left || c_rbnode_parent(n->left) == n);
+
+                /* verify 2) */
+                if (!c_rbnode_parent(n))
+                        c_assert(c_rbnode_is_black(n));
+
+                if (c_rbnode_is_red(n)) {
+                        /* verify 4) */
+                        c_assert(!n->left || c_rbnode_is_black(n->left));
+                        c_assert(!n->right || c_rbnode_is_black(n->right));
+                } else {
+                        /* verify 1) */
+                        c_assert(c_rbnode_is_black(n));
+                }
+
+                /* verify 5) */
+                if (!n->left && !n->right)
+                        c_assert(i_black == n_black);
+
+                /* get next node */
+                if (n->right) {
+                        n = n->right;
+                        if (c_rbnode_is_black(n))
+                                ++i_black;
+
+                        while (n->left) {
+                                n = n->left;
+                                if (c_rbnode_is_black(n))
+                                        ++i_black;
+                        }
+                } else {
+                        while ((p = c_rbnode_parent(n)) && n == p->right) {
+                                n = p;
+                                if (c_rbnode_is_black(p->right))
+                                        --i_black;
+                        }
+
+                        n = p;
+                        if (p && c_rbnode_is_black(p->left))
+                                --i_black;
+                }
+        }
+
+        return count;
+}
+
+static void insert(CRBTree *t, CRBNode *n) {
+        CRBNode **i, *p;
+
+        c_assert(t);
+        c_assert(n);
+        c_assert(!c_rbnode_is_linked(n));
+
+        i = &t->root;
+        p = NULL;
+        while (*i) {
+                p = *i;
+                if (n < *i) {
+                        i = &(*i)->left;
+                } else {
+                        c_assert(n > *i);
+                        i = &(*i)->right;
+                }
+        }
+
+        c_rbtree_add(t, p, i, n);
+}
+
+static void shuffle(CRBNode **nodes, size_t n_memb) {
+        unsigned int i, j;
+        CRBNode *t;
+
+        for (i = 0; i < n_memb; ++i) {
+                j = rand() % n_memb;
+                t = nodes[j];
+                nodes[j] = nodes[i];
+                nodes[i] = t;
+        }
+}
+
+static void test_shuffle(void) {
+        CRBNode *nodes[512];
+        CRBTree t = {};
+        unsigned int i, j;
+        size_t n;
+
+        /* allocate and initialize all nodes */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                nodes[i] = malloc(sizeof(*nodes[i]));
+                c_assert(nodes[i]);
+                c_rbnode_init(nodes[i]);
+        }
+
+        /* shuffle nodes and validate *empty* tree */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+        n = validate(&t);
+        c_assert(n == 0);
+
+        /* add all nodes and validate after each insertion */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                insert(&t, nodes[i]);
+                n = validate(&t);
+                c_assert(n == i + 1);
+        }
+
+        /* shuffle nodes again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* remove all nodes (in different order) and validate on each round */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                c_rbnode_unlink(nodes[i]);
+                n = validate(&t);
+                c_assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
+        }
+
+        /* shuffle nodes and validate *empty* tree again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+        n = validate(&t);
+        c_assert(n == 0);
+
+        /* add all nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                insert(&t, nodes[i]);
+                n = validate(&t);
+                c_assert(n == i + 1);
+        }
+
+        /* 4 times, remove half of the nodes and add them again */
+        for (j = 0; j < 4; ++j) {
+                /* shuffle nodes again */
+                shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+                /* remove half of the nodes */
+                for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
+                        c_rbnode_unlink(nodes[i]);
+                        n = validate(&t);
+                        c_assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
+                }
+
+                /* shuffle the removed half */
+                shuffle(nodes, sizeof(nodes) / sizeof(*nodes) / 2);
+
+                /* add the removed half again */
+                for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
+                        insert(&t, nodes[i]);
+                        n = validate(&t);
+                        c_assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1);
+                }
+        }
+
+        /* shuffle nodes again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* remove all */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                c_rbnode_unlink(nodes[i]);
+                n = validate(&t);
+                c_assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
+        }
+
+        /* free nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                free(nodes[i]);
+}
+
+int main(int argc, char **argv) {
+        unsigned int i;
+
+        /* we want stable tests, so use fixed seed */
+        srand(0xdeadbeef);
+
+        /*
+         * The tests are pseudo random; run them multiple times, each run will
+         * have different orders and thus different results.
+         */
+        for (i = 0; i < 4; ++i)
+                test_shuffle();
+
+        return 0;
+}
diff --git a/src/c-rbtree/src/test-map.c b/src/c-rbtree/src/test-map.c
new file mode 100644
index 00000000..48a300d6
--- /dev/null
+++ b/src/c-rbtree/src/test-map.c
@@ -0,0 +1,277 @@
+/*
+ * RB-Tree based Map
+ * This implements a basic Map between integer keys and objects. It uses the
+ * lookup and insertion helpers, rather than open-coding it.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "c-rbtree.h"
+#include "c-rbtree-private.h"
+
+typedef struct {
+        unsigned long key;
+        unsigned int marker;
+        CRBNode rb;
+} Node;
+
+#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb)))
+
+static int test_compare(CRBTree *t, void *k, CRBNode *n) {
+        unsigned long key = (unsigned long)k;
+        Node *node = node_from_rb(n);
+
+        return (key < node->key) ? -1 : (key > node->key) ? 1 : 0;
+}
+
+static void shuffle(Node **nodes, size_t n_memb) {
+        unsigned int i, j;
+        Node *t;
+
+        for (i = 0; i < n_memb; ++i) {
+                j = rand() % n_memb;
+                t = nodes[j];
+                nodes[j] = nodes[i];
+                nodes[i] = t;
+        }
+}
+
+static void test_map(void) {
+        CRBNode **slot, *p, *safe_p;
+        CRBTree t = {};
+        Node *n, *safe_n, *nodes[2048];
+        unsigned long i, v;
+
+        /* allocate and initialize all nodes */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                nodes[i] = malloc(sizeof(*nodes[i]));
+                c_assert(nodes[i]);
+                nodes[i]->key = i;
+                nodes[i]->marker = 0;
+                c_rbnode_init(&nodes[i]->rb);
+        }
+
+        /* shuffle nodes */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* add all nodes, and verify that each node is linked */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                c_assert(!c_rbnode_is_linked(&nodes[i]->rb));
+                c_assert(!c_rbtree_find_entry(&t, test_compare, (void *)nodes[i]->key, Node, rb));
+
+                slot = c_rbtree_find_slot(&t, test_compare, (void *)nodes[i]->key, &p);
+                c_assert(slot);
+                c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+
+                c_assert(c_rbnode_is_linked(&nodes[i]->rb));
+                c_assert(nodes[i] == c_rbtree_find_entry(&t, test_compare, (void *)nodes[i]->key, Node, rb));
+        }
+
+        /* verify in-order traversal works */
+        i = 0;
+        v = 0;
+        for (p = c_rbtree_first(&t); p; p = c_rbnode_next(p)) {
+                ++i;
+                c_assert(!node_from_rb(p)->marker);
+                node_from_rb(p)->marker = 1;
+
+                c_assert(v <= node_from_rb(p)->key);
+                v = node_from_rb(p)->key;
+
+                c_assert(!c_rbnode_next(p) || p == c_rbnode_prev(c_rbnode_next(p)));
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify reverse in-order traversal works */
+        i = 0;
+        v = -1;
+        for (p = c_rbtree_last(&t); p; p = c_rbnode_prev(p)) {
+                ++i;
+                c_assert(node_from_rb(p)->marker);
+                node_from_rb(p)->marker = 0;
+
+                c_assert(v >= node_from_rb(p)->key);
+                v = node_from_rb(p)->key;
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify post-order traversal works */
+        i = 0;
+        for (p = c_rbtree_first_postorder(&t); p; p = c_rbnode_next_postorder(p)) {
+                ++i;
+                c_assert(!node_from_rb(p)->marker);
+                c_assert(!c_rbnode_parent(p) || !node_from_rb(c_rbnode_parent(p))->marker);
+                c_assert(!p->left || node_from_rb(p->left)->marker);
+                c_assert(!p->right || node_from_rb(p->right)->marker);
+                node_from_rb(p)->marker = 1;
+
+                c_assert(!c_rbnode_next_postorder(p) || p == c_rbnode_prev_postorder(c_rbnode_next_postorder(p)));
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify pre-order (inverse post-order) traversal works */
+        i = 0;
+        for (p = c_rbtree_last_postorder(&t); p; p = c_rbnode_prev_postorder(p)) {
+                ++i;
+                c_assert(node_from_rb(p)->marker);
+                c_assert(!c_rbnode_parent(p) || !node_from_rb(c_rbnode_parent(p))->marker);
+                c_assert(!p->left || node_from_rb(p->left)->marker);
+                c_assert(!p->right || node_from_rb(p->right)->marker);
+                node_from_rb(p)->marker = 0;
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify in-order traversal works via helper */
+        i = 0;
+        v = 0;
+        c_rbtree_for_each(p, &t) {
+                ++i;
+                c_assert(!node_from_rb(p)->marker);
+                node_from_rb(p)->marker = 1;
+
+                c_assert(v <= node_from_rb(p)->key);
+                v = node_from_rb(p)->key;
+
+                c_assert(!c_rbnode_next(p) || p == c_rbnode_prev(c_rbnode_next(p)));
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify in-order traversal works via entry-helper */
+        i = 0;
+        v = 0;
+        c_rbtree_for_each_entry(n, &t, rb) {
+                ++i;
+                c_assert(n->marker);
+                n->marker = 0;
+
+                c_assert(v <= n->key);
+                v = n->key;
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify post-order traversal works via helper */
+        i = 0;
+        c_rbtree_for_each_postorder(p, &t) {
+                ++i;
+                c_assert(!node_from_rb(p)->marker);
+                c_assert(!c_rbnode_parent(p) || !node_from_rb(c_rbnode_parent(p))->marker);
+                c_assert(!p->left || node_from_rb(p->left)->marker);
+                c_assert(!p->right || node_from_rb(p->right)->marker);
+                node_from_rb(p)->marker = 1;
+
+                c_assert(!c_rbnode_next_postorder(p) || p == c_rbnode_prev_postorder(c_rbnode_next_postorder(p)));
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* verify post-order traversal works via entry-helper */
+        i = 0;
+        c_rbtree_for_each_entry_postorder(n, &t, rb) {
+                ++i;
+                c_assert(n->marker);
+                c_assert(!c_rbnode_parent(&n->rb) || node_from_rb(c_rbnode_parent(&n->rb))->marker);
+                c_assert(!n->rb.left || !node_from_rb(n->rb.left)->marker);
+                c_assert(!n->rb.right || !node_from_rb(n->rb.right)->marker);
+                n->marker = 0;
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+
+        /* shuffle nodes again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* remove all nodes (in different order) */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                c_assert(c_rbnode_is_linked(&nodes[i]->rb));
+                c_assert(nodes[i] == c_rbtree_find_entry(&t, test_compare, (void *)nodes[i]->key, Node, rb));
+
+                c_rbnode_unlink(&nodes[i]->rb);
+
+                c_assert(!c_rbnode_is_linked(&nodes[i]->rb));
+                c_assert(!c_rbtree_find_entry(&t, test_compare, (void *)nodes[i]->key, Node, rb));
+        }
+        c_assert(c_rbtree_is_empty(&t));
+
+        /* add all nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                slot = c_rbtree_find_slot(&t, test_compare, (void *)nodes[i]->key, &p);
+                c_assert(slot);
+                c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+        }
+
+        /* remove all nodes via helper */
+        i = 0;
+        c_rbtree_for_each_safe(p, safe_p, &t) {
+                ++i;
+                c_rbnode_unlink(p);
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+        c_assert(c_rbtree_is_empty(&t));
+
+        /* add all nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                slot = c_rbtree_find_slot(&t, test_compare, (void *)nodes[i]->key, &p);
+                c_assert(slot);
+                c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+        }
+
+        /* remove all nodes via entry-helper */
+        i = 0;
+        c_rbtree_for_each_entry_safe(n, safe_n, &t, rb) {
+                ++i;
+                c_rbnode_unlink(&n->rb);
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+        c_assert(c_rbtree_is_empty(&t));
+
+        /* add all nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                slot = c_rbtree_find_slot(&t, test_compare, (void *)nodes[i]->key, &p);
+                c_assert(slot);
+                c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+        }
+
+        /* remove all nodes via unlink-helper */
+        i = 0;
+        c_rbtree_for_each_safe_postorder_unlink(p, safe_p, &t) {
+                ++i;
+                c_assert(!c_rbnode_is_linked(p));
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+        c_assert(c_rbtree_is_empty(&t));
+
+        /* add all nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                slot = c_rbtree_find_slot(&t, test_compare, (void *)nodes[i]->key, &p);
+                c_assert(slot);
+                c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+        }
+
+        /* remove all nodes via entry-unlink-helper */
+        i = 0;
+        c_rbtree_for_each_entry_safe_postorder_unlink(n, safe_n, &t, rb) {
+                ++i;
+                c_assert(!c_rbnode_is_linked(&n->rb));
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+        c_assert(c_rbtree_is_empty(&t));
+
+        /* free nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                c_assert(!nodes[i]->marker);
+                free(nodes[i]);
+        }
+
+        c_assert(c_rbtree_is_empty(&t));
+}
+
+int main(int argc, char **argv) {
+        /* we want stable tests, so use fixed seed */
+        srand(0xdeadbeef);
+
+        test_map();
+        return 0;
+}
diff --git a/src/c-rbtree/src/test-misc.c b/src/c-rbtree/src/test-misc.c
new file mode 100644
index 00000000..ac2717cd
--- /dev/null
+++ b/src/c-rbtree/src/test-misc.c
@@ -0,0 +1,66 @@
+/*
+ * Tests for Miscellaneous Tree Operations
+ * This test contains all of the minor tests that did not fit anywhere else.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-rbtree.h"
+#include "c-rbtree-private.h"
+
+static void insert(CRBTree *t, CRBNode *n) {
+        CRBNode **i, *p;
+
+        c_assert(t);
+        c_assert(n);
+        c_assert(!c_rbnode_is_linked(n));
+
+        i = &t->root;
+        p = NULL;
+        while (*i) {
+                p = *i;
+                if (n < *i) {
+                        i = &(*i)->left;
+                } else {
+                        c_assert(n > *i);
+                        i = &(*i)->right;
+                }
+        }
+
+        c_rbtree_add(t, p, i, n);
+}
+
+static void test_move(void) {
+        CRBTree t1 = C_RBTREE_INIT, t2 = C_RBTREE_INIT;
+        CRBNode n[128];
+        unsigned int i;
+
+        for (i = 0; i < sizeof(n) / sizeof(*n); ++i) {
+                n[i] = (CRBNode)C_RBNODE_INIT(n[i]);
+                insert(&t1, &n[i]);
+        }
+
+        c_assert(!c_rbtree_is_empty(&t1));
+        c_assert(c_rbtree_is_empty(&t2));
+
+        c_rbtree_move(&t2, &t1);
+
+        c_assert(c_rbtree_is_empty(&t1));
+        c_assert(!c_rbtree_is_empty(&t2));
+
+        while (t2.root)
+                c_rbnode_unlink(t2.root);
+
+        c_assert(c_rbtree_is_empty(&t1));
+        c_assert(c_rbtree_is_empty(&t2));
+}
+
+int main(int argc, char **argv) {
+        test_move();
+
+        return 0;
+}
diff --git a/src/c-rbtree/src/test-parallel.c b/src/c-rbtree/src/test-parallel.c
new file mode 100644
index 00000000..4baf8e70
--- /dev/null
+++ b/src/c-rbtree/src/test-parallel.c
@@ -0,0 +1,381 @@
+/*
+ * Tests Lockless Tree Lookups
+ * The RB-Tree implementation supports lockless tree lookups on shared
+ * data-structures. While it does not guarantee correct results (you might skip
+ * entire sub-trees), it does guarantee valid behavior (the traversal is
+ * guaranteed to end and produce some valid result).
+ * This test uses ptrace to run tree operations step-by-step in a separate
+ * process, and after each instruction verify the pseudo-validity of the tree.
+ * This means, a tree must only have valid left/right pointers (or NULL), and
+ * must not contain any loops in those pointers.
+ *
+ * This test runs two processes with a shared context and tree. It runs them in
+ * this order:
+ *
+ *         | PARENT             | CHILD     |
+ *         +--------------------+-----------+
+ *         ~                    ~           ~
+ *          test_parent_start
+ *                               test_child1
+ *          test_parent_middle
+ *                               test_child2
+ *          test_parent_end
+ *         ~                    ~           ~
+ *         +--------------------+-----------+
+ *
+ * Additionally, on each TRAP of CHILD, the parent runs test_parent_step(). The
+ * ptrace infrastructure generates a TRAP after each instruction, so this test
+ * is very CPU aggressive in the parent.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+#include "c-rbtree.h"
+#include "c-rbtree-private.h"
+
+typedef struct {
+        CRBNode rb;
+        bool visited;
+} TestNode;
+
+typedef struct {
+        size_t mapsize;
+        char *map;
+        CRBTree *tree;
+        TestNode *node_mem;
+        CRBNode **nodes;
+        CRBNode **cache;
+        size_t n_nodes;
+} TestContext;
+
+/* avoid ptrace-sigstop by using SIGKILL errors in traced children */
+#define child_assert(_expr) ((void)(!!(_expr) ? 1 : (raise(SIGKILL), 0)))
+
+static int compare(CRBTree *t, void *k, CRBNode *n) {
+        return (char *)n - (char *)k;
+}
+
+static void shuffle(CRBNode **nodes, size_t n_memb) {
+        unsigned int i, j;
+        CRBNode *t;
+
+        for (i = 0; i < n_memb; ++i) {
+                j = rand() % n_memb;
+                t = nodes[j];
+                nodes[j] = nodes[i];
+                nodes[i] = t;
+        }
+}
+
+static void toggle_visit(CRBNode *n, bool set) {
+        c_rbnode_entry(n, TestNode, rb)->visited = set;
+}
+
+static bool fetch_visit(CRBNode *n) {
+        return c_rbnode_entry(n, TestNode, rb)->visited;
+}
+
+static void test_child1(TestContext *ctx) {
+        CRBNode *p, **slot;
+        size_t i;
+
+        for (i = 0; i < ctx->n_nodes; ++i) {
+                child_assert(!c_rbnode_is_linked(ctx->nodes[i]));
+                slot = c_rbtree_find_slot(ctx->tree, compare, ctx->nodes[i], &p);
+                c_rbtree_add(ctx->tree, p, slot, ctx->nodes[i]);
+        }
+}
+
+static void test_child2(TestContext *ctx) {
+        size_t i;
+
+        for (i = 0; i < ctx->n_nodes; ++i) {
+                child_assert(c_rbnode_is_linked(ctx->nodes[i]));
+                c_rbnode_unlink(ctx->nodes[i]);
+        }
+}
+
+static void test_parent_start(TestContext *ctx) {
+        size_t i;
+
+        /*
+         * Generate a tree with @n_nodes entries. We store the entries in
+         * @ctx->node_mem, generate a randomized access-map in @ctx->nodes
+         * (i.e., an array of pointers to entries in @ctx->node_mem, but in
+         * random order), and a temporary cache for free use in the parent.
+         *
+         * All this is stored in a MAP_SHARED memory region so it is equivalent
+         * in child and parent.
+         */
+
+        ctx->n_nodes = 32;
+        ctx->mapsize = sizeof(CRBTree);
+        ctx->mapsize += ctx->n_nodes * sizeof(TestNode);
+        ctx->mapsize += ctx->n_nodes * sizeof(CRBNode*);
+        ctx->mapsize += ctx->n_nodes * sizeof(CRBNode*);
+
+        ctx->map = mmap(NULL, ctx->mapsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+        c_assert(ctx->map != MAP_FAILED);
+
+        ctx->tree = (void *)ctx->map;
+        ctx->node_mem = (void *)(ctx->tree + 1);
+        ctx->nodes = (void *)(ctx->node_mem + ctx->n_nodes);
+        ctx->cache = (void *)(ctx->nodes + ctx->n_nodes);
+
+        for (i = 0; i < ctx->n_nodes; ++i) {
+                ctx->nodes[i] = &ctx->node_mem[i].rb;
+                c_rbnode_init(ctx->nodes[i]);
+        }
+
+        shuffle(ctx->nodes, ctx->n_nodes);
+}
+
+static void test_parent_middle(TestContext *ctx) {
+        size_t i;
+
+        shuffle(ctx->nodes, ctx->n_nodes);
+
+        for (i = 0; i < ctx->n_nodes; ++i)
+                child_assert(c_rbnode_is_linked(ctx->nodes[i]));
+}
+
+static void test_parent_end(TestContext *ctx) {
+        size_t i;
+        int r;
+
+        for (i = 0; i < ctx->n_nodes; ++i)
+                c_assert(!c_rbnode_is_linked(ctx->nodes[i]));
+
+        r = munmap(ctx->map, ctx->mapsize);
+        c_assert(r >= 0);
+}
+
+static void test_parent_step(TestContext *ctx) {
+        size_t i, i_level;
+        CRBNode *n, *p;
+
+        n = ctx->tree->root;
+        i_level = 0;
+
+        while (n) {
+                /* verify that we haven't visited @n, yet */
+                c_assert(!fetch_visit(n));
+
+                /* verify @n is a valid node */
+                for (i = 0; i < ctx->n_nodes; ++i)
+                        if (n == ctx->nodes[i])
+                                break;
+                c_assert(i < ctx->n_nodes);
+
+                /* pre-order traversal and marker for cycle detection */
+                if (n->left) {
+                        toggle_visit(n, true);
+                        ctx->cache[i_level++] = n;
+                        n = n->left;
+                } else if (n->right) {
+                        toggle_visit(n, true);
+                        ctx->cache[i_level++] = n;
+                        n = n->right;
+                } else {
+                        while (i_level > 0) {
+                                p = ctx->cache[i_level - 1];
+                                if (p->right && n != p->right) {
+                                        n = p->right;
+                                        break;
+                                }
+                                --i_level;
+                                n = p;
+                                toggle_visit(n, false);
+                        }
+                        if (i_level == 0)
+                                break;
+                }
+        }
+}
+
+static int test_parallel_child(TestContext *ctx) {
+        int r;
+
+        /*
+         * Make parent trace us and enter stopped state. In case of EPERM, we
+         * are either ptraced already, or are not privileged to run ptrace.
+         * Exit via 0xdf to signal this condition to our parent.
+         */
+        r = ptrace(PTRACE_TRACEME, 0, 0, 0);
+        if (r < 0 && errno == EPERM)
+                return 0xdf;
+
+        child_assert(r >= 0);
+
+        /* SIGUSR1 to signal readiness */
+        r = raise(SIGUSR1);
+        child_assert(r >= 0);
+
+        /* run first part */
+        test_child1(ctx);
+
+        /* SIGURG to cause re-shuffle */
+        r = raise(SIGURG);
+        child_assert(r >= 0);
+
+        /* run second part */
+        test_child2(ctx);
+
+        /* SIGUSR2 to signal end */
+        r = raise(SIGUSR2);
+        child_assert(r >= 0);
+
+        /* return known exit code to parent */
+        return 0xef;
+}
+
+static int test_parallel(void) {
+        TestContext ctx = {};
+        int r, pid, status;
+        uint64_t n_instr, n_event;
+
+        /* create shared area for tree verification */
+        test_parent_start(&ctx);
+
+        /* run child */
+        pid = fork();
+        c_assert(pid >= 0);
+        if (pid == 0) {
+                r = test_parallel_child(&ctx);
+                _exit(r);
+        }
+
+        /*
+         * After setup, the child immediately enters TRACE-operation and raises
+         * SIGUSR1. Once continued, the child performs the pre-configured tree
+         * operations. When done, it raises SIGUSR2, and then exits.
+         *
+         * Here in the parent we catch all trace-stops of the child via waitpid
+         * until we get no more such stop-events. Based on the stop-event we
+         * get, we verify child-state, STEP it, or perform other state tracking.
+         * We repeat this as long as we catch trace-stops from the child.
+         */
+        n_instr = 0;
+        n_event = 0;
+        for (r = waitpid(pid, &status, 0);
+             r == pid && WIFSTOPPED(status);
+             r = waitpid(pid, &status, 0)) {
+
+                switch (WSTOPSIG(status)) {
+                case SIGUSR1:
+                        n_event |= 0x1;
+
+                        /* step child */
+                        r = ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
+
+                        /*
+                         * Some architectures (e.g., armv7hl) do not implement
+                         * SINGLESTEP, but return EIO. Skip the entire test in
+                         * this case.
+                         */
+                        if (r < 0 && errno == EIO)
+                                return 77;
+
+                        c_assert(r >= 0);
+                        break;
+
+                case SIGURG:
+                        n_event |= 0x2;
+                        test_parent_middle(&ctx);
+
+                        /* step child */
+                        r = ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
+                        c_assert(r >= 0);
+                        break;
+
+                case SIGUSR2:
+                        n_event |= 0x4;
+                        test_parent_end(&ctx);
+
+                        /* continue child */
+                        r = ptrace(PTRACE_CONT, pid, 0, 0);
+                        c_assert(r >= 0);
+                        break;
+
+                case SIGTRAP:
+                        ++n_instr;
+                        test_parent_step(&ctx);
+
+                        /* step repeatedly as long as we get SIGTRAP */
+                        r = ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
+                        c_assert(r >= 0);
+                        break;
+
+                default:
+                        c_assert(0);
+                        break;
+                }
+        }
+
+        /* verify our child exited cleanly */
+        c_assert(r == pid);
+        c_assert(!!WIFEXITED(status));
+
+        /*
+         * 0xdf is signalled if ptrace is not allowed or we are already
+         * ptraced. In this case we skip the test.
+         *
+         * 0xef is signalled on success.
+         *
+         * In any other case something went wobbly and we should fail hard.
+         */
+        switch (WEXITSTATUS(status)) {
+        case 0xef:
+                break;
+        case 0xdf:
+                return 77;
+        default:
+                c_assert(0);
+                break;
+        }
+
+        /* verify we hit all child states */
+        c_assert(n_event & 0x1);
+        c_assert(n_event & 0x2);
+        c_assert(n_event & 0x4);
+        c_assert(n_instr > 0);
+
+        return 0;
+}
+
+int main(int argc, char **argv) {
+        unsigned int i;
+        int r;
+
+        /* we want stable tests, so use fixed seed */
+        srand(0xdeadbeef);
+
+        /*
+         * The tests are pseudo random; run them multiple times, each run will
+         * have different orders and thus different results.
+         */
+        for (i = 0; i < 4; ++i) {
+                r = test_parallel();
+                if (r)
+                        return r;
+        }
+
+        return 0;
+}
diff --git a/src/c-rbtree/src/test-posix.c b/src/c-rbtree/src/test-posix.c
new file mode 100644
index 00000000..ee100ec5
--- /dev/null
+++ b/src/c-rbtree/src/test-posix.c
@@ -0,0 +1,270 @@
+/*
+ * Tests to compare against POSIX RB-Trees
+ * POSIX provides balanced binary trees via the tsearch(3p) API. glibc
+ * implements them as RB-Trees. This file compares the performance of both.
+ *
+ * The semantic differences are:
+ *
+ *   o The tsearch(3p) API does memory allocation of node structures itself,
+ *     rather than allowing the caller to embed it.
+ *
+ *   o The c-rbtree API exposes the tree structure, allowing efficient tree
+ *     operations. Furthermore, it allows tree creation/deletion without taking
+ *     the expensive insert/remove paths. For instance, imagine you want to
+ *     create an rb-tree from a set of objects you have. With c-rbtree you can
+ *     do that without a single rotation or tree-restructuring in O(n), while
+ *     tsearch(3p) requires O(n log n).
+ *
+ *   o The tsearch(3p) API requires one pointer-chase on each node access. This
+ *     is inherent to the design as it does not allow embedding the node in the
+ *     parent object. This slows down the API considerably.
+ *
+ *   o The tsearch(3p) API does not allow multiple entries with the same key.
+ *
+ *   o The tsearch(3p) API requires node lookup during removal. This does not
+ *     affect the worst-case runtime, but does reduce absolute performance.
+ *
+ *   o The tsearch(3p) API does not allow O(1) tests whether a node is linked
+ *     or not. It requires a separate state variable per node.
+ *
+ *   o The tsearch(3p) API does not allow walking the tree with context. The
+ *     only accessor twalk(3p) provides no tree context nor caller context to
+ *     the callback function.
+ *
+ *   o The glibc implementation of tsearch(3p) uses RB-Trees without parent
+ *     pointers. Hence, tree traversal requires back-tracking. Performance is
+ *     similar, but it reduces memory consumption (though, at the same time it
+ *     stores the key pointer, and allocates the node on the heap, so overall
+ *     the memory consumption is higher still).
+ *     But the more important issue is, a node itself is not enough context as
+ *     tree iterator, but the full depth parent pointers are needed as well.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <search.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "c-rbtree.h"
+#include "c-rbtree-private.h"
+
+typedef struct {
+        int key;
+        CRBNode rb;
+} Node;
+
+#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb)))
+#define node_from_key(_key) ((Node *)((char *)(_key) - offsetof(Node, key)))
+
+static void shuffle(Node **nodes, size_t n_memb) {
+        unsigned int i, j;
+        Node *t;
+
+        for (i = 0; i < n_memb; ++i) {
+                j = rand() % n_memb;
+                t = nodes[j];
+                nodes[j] = nodes[i];
+                nodes[i] = t;
+        }
+}
+
+static int compare(CRBTree *t, void *k, CRBNode *n) {
+        int key = (int)(unsigned long)k;
+        Node *node = node_from_rb(n);
+
+        return key - node->key;
+}
+
+static uint64_t now(void) {
+        struct timespec ts;
+        int r;
+
+        r = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+        c_assert(r >= 0);
+        return ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
+}
+
+/*
+ * POSIX tsearch(3p) based RB-Tree API
+ *
+ * This implements a small rb-tree API alongside c-rbtree but based on
+ * tsearch(3p) and friends.
+ *
+ * Note that we don't care for OOM here, nor do we implement all the same
+ * features as c-rbtree. This just does basic insertion, removal, and lookup
+ * without any conflict detection.
+ *
+ * This also hard-codes 'Node' as object type that can be stored in the tree.
+ */
+
+typedef struct PosixRBTree PosixRBTree;
+
+struct PosixRBTree {
+        void *root;
+};
+
+static int posix_rbtree_compare(const void *a, const void *b) {
+        return *(const int *)a - *(const int *)b;
+}
+
+static void posix_rbtree_add(PosixRBTree *t, const Node *node) {
+        void *res;
+
+        res = tsearch(&node->key, &t->root, posix_rbtree_compare);
+        c_assert(*(int **)res == &node->key);
+}
+
+static void posix_rbtree_remove(PosixRBTree *t, const Node *node) {
+        void *res;
+
+        res = tdelete(&node->key, &t->root, posix_rbtree_compare);
+        c_assert(res);
+}
+
+static Node *posix_rbtree_find(PosixRBTree *t, int key) {
+        void *res;
+
+        res = tfind(&key, &t->root, posix_rbtree_compare);
+        return res ? node_from_key(*(int **)res) : NULL;
+}
+
+static void posix_rbtree_visit(const void *n, const VISIT o, const int depth) {
+        static int v;
+
+        /* HACK: twalk() has no context; use static context; reset on root */
+        if (depth == 0 && (o == preorder || o == leaf))
+                v = 0;
+
+        switch (o) {
+        case postorder:
+        case leaf:
+                c_assert(v <= node_from_key(*(int **)n)->key);
+                v = node_from_key(*(int **)n)->key;
+                break;
+        default:
+                break;
+        }
+}
+
+static void posix_rbtree_traverse(PosixRBTree *t) {
+        twalk(t->root, posix_rbtree_visit);
+}
+
+/*
+ * Comparison between c-rbtree and tsearch(3p)
+ *
+ * Based on the tsearch(3p) API above, this now implements some comparisons
+ * between c-rbtree and the POSIX API.
+ *
+ * The semantic differences are explained above. This does mostly performance
+ * comparisons.
+ */
+
+static void test_posix(void) {
+        uint64_t ts, ts_c1, ts_c2, ts_c3, ts_c4;
+        uint64_t ts_p1, ts_p2, ts_p3, ts_p4;
+        PosixRBTree pt = {};
+        CRBNode **slot, *p;
+        CRBTree t = {};
+        Node *nodes[2048];
+        unsigned long i;
+        int v;
+
+        /* allocate and initialize all nodes */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                nodes[i] = malloc(sizeof(*nodes[i]));
+                c_assert(nodes[i]);
+                nodes[i]->key = i;
+                c_rbnode_init(&nodes[i]->rb);
+        }
+
+        /* shuffle nodes */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* add all nodes, and verify that each node is linked */
+        ts = now();
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
+                slot = c_rbtree_find_slot(&t, compare, (void *)(unsigned long)nodes[i]->key, &p);
+                c_assert(slot);
+                c_rbtree_add(&t, p, slot, &nodes[i]->rb);
+        }
+        ts_c1 = now() - ts;
+
+        ts = now();
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                posix_rbtree_add(&pt, nodes[i]);
+        ts_p1 = now() - ts;
+
+        /* shuffle nodes again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* traverse tree in-order */
+        ts = now();
+        i = 0;
+        v = 0;
+        for (p = c_rbtree_first(&t); p; p = c_rbnode_next(p)) {
+                ++i;
+
+                c_assert(v <= node_from_rb(p)->key);
+                v = node_from_rb(p)->key;
+        }
+        c_assert(i == sizeof(nodes) / sizeof(*nodes));
+        ts_c2 = now() - ts;
+
+        ts = now();
+        posix_rbtree_traverse(&pt);
+        ts_p2 = now() - ts;
+
+        /* shuffle nodes again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* lookup all nodes (in different order) */
+        ts = now();
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                c_assert(nodes[i] == c_rbtree_find_entry(&t, compare,
+                                                       (void *)(unsigned long)nodes[i]->key,
+                                                       Node, rb));
+        ts_c3 = now() - ts;
+
+        ts = now();
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                c_assert(nodes[i] == posix_rbtree_find(&pt, nodes[i]->key));
+        ts_p3 = now() - ts;
+
+        /* shuffle nodes again */
+        shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
+
+        /* remove all nodes (in different order) */
+        ts = now();
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                c_rbnode_unlink(&nodes[i]->rb);
+        ts_c4 = now() - ts;
+
+        ts = now();
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                posix_rbtree_remove(&pt, nodes[i]);
+        ts_p4 = now() - ts;
+
+        /* free nodes again */
+        for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
+                free(nodes[i]);
+
+        fprintf(stderr, "              insertion  traversal     lookup    removal\n");
+        fprintf(stderr, "   c-rbtree: %8"PRIu64"ns %8"PRIu64"ns %8"PRIu64"ns %8"PRIu64"ns\n",
+                ts_c1, ts_c2, ts_c3, ts_c4);
+        fprintf(stderr, "tsearch(3p): %8"PRIu64"ns %8"PRIu64"ns %8"PRIu64"ns %8"PRIu64"ns\n",
+                ts_p1, ts_p2, ts_p3, ts_p4);
+}
+
+int main(int argc, char **argv) {
+        /* we want stable tests, so use fixed seed */
+        srand(0xdeadbeef);
+
+        test_posix();
+        return 0;
+}
diff --git a/src/c-rbtree/subprojects/libcstdaux-1 b/src/c-rbtree/subprojects/libcstdaux-1
new file mode 120000
index 00000000..589984f3
--- /dev/null
+++ b/src/c-rbtree/subprojects/libcstdaux-1
@@ -0,0 +1 @@
+../../c-stdaux
\ No newline at end of file
diff --git a/src/c-siphash/.editorconfig b/src/c-siphash/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/c-siphash/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 8
diff --git a/src/c-siphash/.github/workflows/ci.yml b/src/c-siphash/.github/workflows/ci.yml
new file mode 100644
index 00000000..00bca960
--- /dev/null
+++ b/src/c-siphash/.github/workflows/ci.yml
@@ -0,0 +1,32 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  ci-linux:
+    name: Linux CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: true
+      m32: true
+      matrixmode: true
+      valgrind: true
+  ci-macos:
+    name: MacOS CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      macos: true
+  ci-windows:
+    name: Windows CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      windows: true
diff --git a/src/c-siphash/.gitmodules b/src/c-siphash/.gitmodules
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/c-siphash/.gitmodules
diff --git a/src/c-siphash/AUTHORS b/src/c-siphash/AUTHORS
new file mode 100644
index 00000000..ee6d914f
--- /dev/null
+++ b/src/c-siphash/AUTHORS
@@ -0,0 +1,38 @@
+LICENSE:
+        This project is dual-licensed under both the Apache License, Version
+        2.0, and the GNU Lesser General Public License, Version 2.1+.
+
+AUTHORS-ASL:
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+                http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+
+AUTHORS-LGPL:
+        This program is free software; you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published
+        by the Free Software Foundation; either version 2.1 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public License
+        along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+COPYRIGHT: (ordered alphabetically)
+        Copyright (C) 2015-2022 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        Daniele Nicolodi <daniele@grinta.net>
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Tom Gundersen <teg@jklm.no>
diff --git a/src/c-siphash/NEWS.md b/src/c-siphash/NEWS.md
new file mode 100644
index 00000000..a0227c04
--- /dev/null
+++ b/src/c-siphash/NEWS.md
@@ -0,0 +1,9 @@
+# c-siphash - Streaming-capable SipHash Implementation
+
+## CHANGES WITH 1.0.0:
+
+        * Initial release of c-siphash.
+
+        Contributions from: David Rheinsberg, Tom Gundersen
+
+        - Brno, 2022-06-22
diff --git a/src/c-siphash/README.md b/src/c-siphash/README.md
new file mode 100644
index 00000000..bd97d06b
--- /dev/null
+++ b/src/c-siphash/README.md
@@ -0,0 +1,53 @@
+c-siphash
+=========
+
+Streaming-capable SipHash Implementation
+
+The c-siphash project is a standalone implementation of SipHash in Standard
+ISO-C11. It provides a streaming-capable API to compute data hashes according
+to the SipHash algorithm. For API documentation, see the c-siphash.h header
+file, as well as the docbook comments for each function.
+
+### Project
+
+ * **Website**: <https://c-util.github.io/c-siphash>
+ * **Bug Tracker**: <https://github.com/c-util/c-siphash/issues>
+
+### Requirements
+
+The requirements for this project are:
+
+ * `libc` (e.g., `glibc >= 2.16`)
+
+At build-time, the following software is required:
+
+ * `meson >= 0.60`
+ * `pkg-config >= 0.29`
+
+### Build
+
+The meson build-system is used for this project. Contact upstream
+documentation for detailed help. In most situations the following
+commands are sufficient to build and install from source:
+
+```sh
+mkdir build
+cd build
+meson setup ..
+ninja
+meson test
+ninja install
+```
+
+No custom configuration options are available.
+
+### Repository:
+
+ - **web**:   <https://github.com/c-util/c-siphash>
+ - **https**: `https://github.com/c-util/c-siphash.git`
+ - **ssh**:   `git@github.com:c-util/c-siphash.git`
+
+### License:
+
+ - **Apache-2.0** OR **LGPL-2.1-or-later**
+ - See AUTHORS file for details.
diff --git a/src/c-siphash/meson.build b/src/c-siphash/meson.build
new file mode 100644
index 00000000..ffcb6a76
--- /dev/null
+++ b/src/c-siphash/meson.build
@@ -0,0 +1,21 @@
+project(
+        'c-siphash',
+        'c',
+        default_options: [
+                'c_std=c11'
+        ],
+        license: 'Apache',
+        meson_version: '>=0.60.0',
+        version: '1.0.0',
+)
+major = meson.project_version().split('.')[0]
+project_description = 'Streaming-capable SipHash Implementation'
+
+mod_pkgconfig = import('pkgconfig')
+
+dep_cstdaux = dependency('libcstdaux-1')
+add_project_arguments(dep_cstdaux.get_variable('cflags').split(' '), language: 'c')
+
+subdir('src')
+
+meson.override_dependency('libcsiphash-'+major, libcsiphash_dep, static: true)
diff --git a/src/c-siphash/src/libcsiphash.def b/src/c-siphash/src/libcsiphash.def
new file mode 100644
index 00000000..445cb0c4
--- /dev/null
+++ b/src/c-siphash/src/libcsiphash.def
@@ -0,0 +1,9 @@
+LIBRARY csiphash-1-0
+EXPORTS
+        c_siphash_init
+        c_siphash_append
+        c_siphash_finalize
+        c_siphash_hash
+        c_siphash_append_13
+        c_siphash_finalize_13
+        c_siphash_hash_13
diff --git a/src/c-siphash/src/libcsiphash.sym b/src/c-siphash/src/libcsiphash.sym
new file mode 100644
index 00000000..aad193d0
--- /dev/null
+++ b/src/c-siphash/src/libcsiphash.sym
@@ -0,0 +1,16 @@
+LIBCSIPHASH_1 {
+global:
+        c_siphash_init;
+        c_siphash_append;
+        c_siphash_finalize;
+        c_siphash_hash;
+local:
+       *;
+};
+
+LIBCSIPHASH_2 {
+global:
+        c_siphash_append_13;
+        c_siphash_finalize_13;
+        c_siphash_hash_13;
+} LIBCSIPHASH_1;
diff --git a/src/c-siphash/src/meson.build b/src/c-siphash/src/meson.build
new file mode 100644
index 00000000..f2cc1864
--- /dev/null
+++ b/src/c-siphash/src/meson.build
@@ -0,0 +1,58 @@
+#
+# target: libcsiphash.so
+#
+
+libcsiphash_deffile = join_paths(meson.current_source_dir(), 'libcsiphash.def')
+libcsiphash_symfile = join_paths(meson.current_source_dir(), 'libcsiphash.sym')
+
+libcsiphash_deps = [
+        dep_cstdaux,
+]
+
+libcsiphash_both = both_libraries(
+        'csiphash-'+major,
+        [
+                'c-siphash.c',
+        ],
+        c_args: [
+                '-fvisibility=hidden',
+                '-fno-common',
+        ],
+        dependencies: libcsiphash_deps,
+        install: not meson.is_subproject(),
+        link_args: dep_cstdaux.get_variable('version-scripts') == 'yes' ? [
+                '-Wl,--version-script=@0@'.format(libcsiphash_symfile),
+        ] : [],
+        link_depends: libcsiphash_symfile,
+        vs_module_defs: libcsiphash_deffile,
+        soversion: 0,
+)
+
+libcsiphash_dep = declare_dependency(
+        dependencies: libcsiphash_deps,
+        include_directories: include_directories('.'),
+        link_with: libcsiphash_both.get_static_lib(),
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers('c-siphash.h')
+
+        mod_pkgconfig.generate(
+                description: project_description,
+                filebase: 'libcsiphash-'+major,
+                libraries: libcsiphash_both.get_shared_lib(),
+                name: 'libcsiphash',
+                version: meson.project_version(),
+        )
+endif
+
+#
+# target: test-*
+#
+
+test_api = executable('test-api', ['test-api.c'], link_with: libcsiphash_both.get_shared_lib())
+test('API Symbol Visibility', test_api)
+
+test_basic = executable('test-basic', ['test-basic.c'], dependencies: libcsiphash_dep)
+test('Basic API Behavior', test_basic)
diff --git a/src/c-siphash/src/test-api.c b/src/c-siphash/src/test-api.c
new file mode 100644
index 00000000..8075a17f
--- /dev/null
+++ b/src/c-siphash/src/test-api.c
@@ -0,0 +1,31 @@
+/*
+ * Tests for Public API
+ * This test, unlikely the others, is linked against the real, distributed,
+ * shared library. Its sole purpose is to test for symbol availability.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-siphash.h"
+
+static void test_api(void) {
+        CSipHash state = C_SIPHASH_NULL;
+        uint8_t seed[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+        uint64_t hash1, hash2;
+
+        c_siphash_init(&state, seed);
+        c_siphash_append(&state, NULL, 0);
+        hash1 = c_siphash_finalize(&state);
+        assert(hash1 == 12552310112479190712ULL);
+
+        hash2 = c_siphash_hash(seed, NULL, 0);
+        assert(hash1 == hash2);
+}
+
+int main(int argc, char **argv) {
+        test_api();
+        return 0;
+}
diff --git a/src/c-siphash/src/test-basic.c b/src/c-siphash/src/test-basic.c
new file mode 100644
index 00000000..58442dac
--- /dev/null
+++ b/src/c-siphash/src/test-basic.c
@@ -0,0 +1,120 @@
+/*
+ * Tests for Basic Hash Operations
+ * This test does some basic hash operations and verifies their correctness. It
+ * breaks up the data to be hashed in various ways to make sure it is stable.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-siphash.h"
+
+/* See https://131002.net/siphash/siphash.pdf, Appendix A. */
+static void do_reference_test(const uint8_t *in, size_t len, const uint8_t *key) {
+        CSipHash state = C_SIPHASH_NULL;
+        uint64_t out;
+        unsigned i, j;
+
+        /* verify the internal state as given in the above paper */
+        c_siphash_init(&state, key);
+        c_assert(state.v0 == 0x7469686173716475);
+        c_assert(state.v1 == 0x6b617f6d656e6665);
+        c_assert(state.v2 == 0x6b7f62616d677361);
+        c_assert(state.v3 == 0x7b6b696e727e6c7b);
+        c_siphash_append(&state, in, len);
+        c_assert(state.v0 == 0x4a017198de0a59e0);
+        c_assert(state.v1 == 0x0d52f6f62a4f59a4);
+        c_assert(state.v2 == 0x634cb3577b01fd3d);
+        c_assert(state.v3 == 0xa5224d6f55c7d9c8);
+        out = c_siphash_finalize(&state);
+        c_assert(out == 0xa129ca6149be45e5);
+        c_assert(state.v0 == 0xf6bcd53893fecff1);
+        c_assert(state.v1 == 0x54b9964c7ea0d937);
+        c_assert(state.v2 == 0x1b38329c099bb55a);
+        c_assert(state.v3 == 0x1814bb89ad7be679);
+
+        /* verify that decomposing the input in three chunks gives the
+           same result */
+        for (i = 0; i < len; i++) {
+                for (j = i; j < len; j++) {
+                        c_siphash_init(&state, key);
+                        c_siphash_append(&state, in, i);
+                        c_siphash_append(&state, &in[i], j - i);
+                        c_siphash_append(&state, &in[j], len - j);
+                        out = c_siphash_finalize(&state);
+                        c_assert(out == 0xa129ca6149be45e5);
+                }
+        }
+
+        /* verify c_siphash_hash() produces the same result */
+        c_assert(out == c_siphash_hash(key, in, len));
+}
+
+static void test_reference(void) {
+
+        const uint8_t in[15]  = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                                  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e };
+        const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                                  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+        uint8_t in_buf[20];
+
+        /* Test with same input but different alignments. */
+        memcpy(in_buf, in, sizeof(in));
+        do_reference_test(in_buf, sizeof(in), key);
+        memcpy(in_buf + 1, in, sizeof(in));
+        do_reference_test(in_buf + 1, sizeof(in), key);
+        memcpy(in_buf + 2, in, sizeof(in));
+        do_reference_test(in_buf + 2, sizeof(in), key);
+        memcpy(in_buf + 4, in, sizeof(in));
+        do_reference_test(in_buf + 4, sizeof(in), key);
+}
+
+static void test_short_hashes(void) {
+        const uint8_t one[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                                0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
+        const uint8_t  key[16] = { 0x22, 0x24, 0x41, 0x22, 0x55, 0x77, 0x88, 0x07,
+                                   0x23, 0x09, 0x23, 0x14, 0x0c, 0x33, 0x0e, 0x0f};
+        uint8_t two[sizeof one] = {0};
+
+        CSipHash state1 = C_SIPHASH_NULL, state2 = C_SIPHASH_NULL;
+        unsigned i, j;
+
+        c_siphash_init(&state1, key);
+        c_siphash_init(&state2, key);
+
+        /* hashing 1, 2, 3, 4, 5, ..., 16 bytes, with the byte after the buffer different */
+        for (i = 1; i <= sizeof one; i++) {
+                c_siphash_append(&state1, one, i);
+
+                two[i-1] = one[i-1];
+                c_siphash_append(&state2, two, i);
+
+                c_assert(memcmp(&state1, &state2, sizeof state1) == 0);
+        }
+
+        /* hashing n and 1, n and 2, n and 3, ..., n-1 and 1, n-2 and 2, ... */
+        for (i = sizeof one; i > 0; i--) {
+                memset(two, 0, sizeof(two));
+
+                for (j = 1; j <= sizeof one; j++) {
+                        c_siphash_append(&state1, one, i);
+                        c_siphash_append(&state1, one, j);
+
+                        c_siphash_append(&state2, one, i);
+                        two[j-1] = one[j-1];
+                        c_siphash_append(&state2, two, j);
+
+                        c_assert(memcmp(&state1, &state2, sizeof state1) == 0);
+                }
+        }
+}
+
+int main(int argc, char *argv[]) {
+        test_reference();
+        test_short_hashes();
+
+        return 0;
+}
diff --git a/src/c-siphash/subprojects/libcstdaux-1 b/src/c-siphash/subprojects/libcstdaux-1
new file mode 120000
index 00000000..589984f3
--- /dev/null
+++ b/src/c-siphash/subprojects/libcstdaux-1
@@ -0,0 +1 @@
+../../c-stdaux
\ No newline at end of file
diff --git a/src/c-stdaux/.editorconfig b/src/c-stdaux/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/c-stdaux/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 8
diff --git a/src/c-stdaux/.github/workflows/ci.yml b/src/c-stdaux/.github/workflows/ci.yml
new file mode 100644
index 00000000..70a673f7
--- /dev/null
+++ b/src/c-stdaux/.github/workflows/ci.yml
@@ -0,0 +1,37 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  ci-linux:
+    name: Linux CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: true
+      m32: true
+      matrixmode: true
+      valgrind: true
+  ci-macos:
+    name: MacOS CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      macos: true
+  ci-windows:
+    name: Windows CI
+    uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
+    with:
+      cabuild_ref: "v1"
+      linux: false
+      windows: true
+  ci-docs:
+    name: Documentation CI
+    uses: bus1/cabuild/.github/workflows/ci-sphinx.yml@v1
+    with:
+      source: "./src/docs"
diff --git a/src/c-stdaux/.readthedocs.yaml b/src/c-stdaux/.readthedocs.yaml
new file mode 100644
index 00000000..5bbe6ac1
--- /dev/null
+++ b/src/c-stdaux/.readthedocs.yaml
@@ -0,0 +1,20 @@
+# Read the Docs configuration file
+
+version: 2
+
+build:
+  apt_packages:
+  - "clang"
+  os: "ubuntu-22.04"
+  tools:
+    python: "3"
+
+formats: "all"
+
+python:
+  install:
+  - requirements: "src/docs/requirements.txt"
+  system_packages: true
+
+sphinx:
+  configuration: "src/docs/conf.py"
diff --git a/src/c-stdaux/AUTHORS b/src/c-stdaux/AUTHORS
new file mode 100644
index 00000000..0f2a73fd
--- /dev/null
+++ b/src/c-stdaux/AUTHORS
@@ -0,0 +1,43 @@
+LICENSE:
+        This project is dual-licensed under both the Apache License, Version
+        2.0, and the GNU Lesser General Public License, Version 2.1+.
+
+AUTHORS-ASL:
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+                http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+
+AUTHORS-LGPL:
+        This program is free software; you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published
+        by the Free Software Foundation; either version 2.1 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public License
+        along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+COPYRIGHT: (ordered alphabetically)
+        Copyright (C) 2018-2022 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Evgeny Vereshchagin <evvers@ya.ru>
+        Jan Engelhardt <jengelh@inai.de>
+        Lorenzo Arena <lorenzo.arena@powersoft.com>
+        Michele Dionisio <michele.dionisio@gmail.com>
+        Thomas Haller <thaller@redhat.com>
+        Tom Gundersen <teg@jklm.no>
+        Yuri Chornoivan <yurchor@ukr.net>
diff --git a/src/c-stdaux/NEWS.md b/src/c-stdaux/NEWS.md
new file mode 100644
index 00000000..8e7b3c71
--- /dev/null
+++ b/src/c-stdaux/NEWS.md
@@ -0,0 +1,106 @@
+# c-stdaux - Auxiliary macros and functions for the C standard library
+
+## CHANGES WITH 1.4.0:
+
+        * New compiler-builtin c_assume_aligned() allows hinting alignment
+          to the compiler and thus improving code generation. For targets
+          without such builtins, the function will be a no-op.
+
+        * A new set of memory-load operations is added: c_load_*()
+          This includes support for reading unaligned & aligned memory,
+          big-endian & little-endian data, and various standard sizes.
+          The helpers are basically a pointer cast to `uintX_t*` and a
+          dereference operation, but they guarantee that strict aliasing
+          rules, as well as alignment requirements are followed.
+
+        Contributions from: David Rheinsberg, Jan Engelhardt, Tom Gundersen
+
+        - Dußlingen, 2023-01-12
+
+## CHANGES WITH 1.3.0:
+
+        * Microsoft Windows is now supported as a target platform.
+
+        * The `C_COMPILER_*` and `C_OS_*` pre-processor constants now
+          allow identifying the used compiler as well as the target OS.
+
+        * The new `_c_always_inline_` annotation allows telling compilers
+          to inline a function unless technically not possible.
+
+        * Split c-stdaux.h into modules and include them from the root
+          header for backwards compatibility. Inclusion of the new modules
+          is guarded by the `C_COMPILER_*` and `C_OS_*` macros to prevent
+          them from being used on unspported platforms. A direct include
+          of the respective modules allows overriding that behavior.
+
+          The new modules provide the same functionality as before on the
+          previously supported linux platforms. With the support of other
+          platforms, individual modules might not be available, or generic
+          functions might provide a stub that provides the same runtime
+          behavior, but possibly with fewer diagnostics.
+
+        * Rework `c_assert()` to avoid context-expressions and instead use
+          the ternary-operator to check for the assertion.
+
+        * Improve `c_{un,}likely()` to support constant-folding as well as
+          -Wparantheses diagnostics if supported by the compiler. This adds
+          `_c_boolean_expr_()` as a helper to achieve this.
+
+        Contributions from: David Rheinsberg, Thomas Haller
+
+        - Dußlingen, 2022-12-15
+
+## CHANGES WITH 1.2.0:
+
+        * Add c_memcmp() as a safe wrapper around memcmp(3) that supports
+          empty arenas as NULL pointers.
+
+        * Add an API documentation renderer based on the sphinx docutils
+          suite. The documentation is available on readthedocs.org.
+
+        * Drop stdatomic.h from the public includes. This was not used by
+          any of the dependent projects, but breaks builds on older GCC
+          compilers. While this is technically an API break, no breakage
+          has been discovered in our tests, and thus we deemed it reasonable
+          to proceed without version bump.
+
+        Contributions from: David Rheinsberg, Thomas Haller
+
+        - Dußlingen, 2022-07-22
+
+## CHANGES WITH 1.1.0:
+
+        * Add c_memcpy() as a safe wrapper around memcpy(3) that supports
+          empty arenas as NULL pointers.
+
+        * Support building on MacOS-X.
+
+        * Rework the apidoc comments and properly document the entire API.
+
+        * Export 'version-scripts' configuration variable alongside the
+          existing 'cflags' variable. It defines whether c-stdaux was built
+          with GNU-linker version-scripts, or not. Dependent projects can
+          use this to decide whether to use version-scripts or not.
+          Additionally, the new 'version-scripts' meson-option allows
+          specifying whether to use version-scripts, auto-detect whether to
+          enable it, or disable it.
+
+        * Fix the export of `cflags` to also be exported in pkg-config, not
+          just meson subprojects.
+
+        * Avoid NULL-pointers in compile-time macros. This silences possible
+          false-positives from code sanitizers that otherwise trip over the
+          NULL pointer dereferences.
+
+        Contributions from: David Rheinsberg, Evgeny Vereshchagin
+
+        - Brno, 2022-06-22
+
+## CHANGES WITH 1.0.0:
+
+        * Initial release of c-stdaux.
+
+        Contributions from: David Rheinsberg, Lorenzo Arena, Michele Dionisio,
+                            Yuri Chornoivan
+
+        - Dußlingen, 2022-05-12
diff --git a/src/c-stdaux/README.md b/src/c-stdaux/README.md
new file mode 100644
index 00000000..985193c5
--- /dev/null
+++ b/src/c-stdaux/README.md
@@ -0,0 +1,54 @@
+c-stdaux
+========
+
+Auxiliary macros and functions for the C standard library
+
+The c-stdaux project contains support macros and auxiliary functions around the
+functionality of common C standard libraries. This includes helpers for the
+ISO C Standard Library, but also other common specifications like POSIX or
+common extended features of widespread compilers like gcc and clang.
+
+### Project
+
+ * **Website**: <https://c-util.github.io/c-stdaux>
+ * **Documentation**: <https://c-stdaux.readthedocs.io>
+ * **Bug Tracker**: <https://github.com/c-util/c-stdaux/issues>
+
+### Requirements
+
+The requirements for this project are:
+
+ * `libc` (e.g., `glibc >= 2.16`)
+
+At build time, the following software is required:
+
+ * `meson >= 0.60`
+ * `pkg-config >= 0.29`
+
+### Build
+
+The meson build system is used for this project. Contact upstream
+documentation for detailed help. In most situations, the following
+commands are sufficient to build and install from source:
+
+```sh
+mkdir build
+cd build
+meson setup ..
+ninja
+meson test
+ninja install
+```
+
+No custom configuration options are available.
+
+### Repository:
+
+ - **web**:   <https://github.com/c-util/c-stdaux>
+ - **https**: `https://github.com/c-util/c-stdaux.git`
+ - **ssh**:   `git@github.com:c-util/c-stdaux.git`
+
+### License:
+
+ - **Apache-2.0** OR **LGPL-2.1-or-later**
+ - See AUTHORS file for details.
diff --git a/src/c-stdaux/meson.build b/src/c-stdaux/meson.build
new file mode 100644
index 00000000..6af822be
--- /dev/null
+++ b/src/c-stdaux/meson.build
@@ -0,0 +1,110 @@
+#
+# Global Project Setup
+#
+
+project(
+        'c-stdaux',
+        'c',
+        default_options: [
+                'c_std=c11'
+        ],
+        license: 'Apache',
+        meson_version: '>=0.60.0',
+        version: '1.4.0',
+)
+major = meson.project_version().split('.')[0]
+project_description = 'Auxiliary macros and functions for the C standard library'
+
+mod_pkgconfig = import('pkgconfig')
+
+#
+# CFLAGS
+#
+# We have a set of compiler flags for GCC and CLANG which adjust their warnings
+# and behavior to our coding-style.
+#
+# This variable is exported to dependent projects via meson for them to use as
+# well. Since these exports are limited to strings, we need to be careful that
+# the individual entries do not contain spaces (see the assertion below).
+#
+
+cflags = meson.get_compiler('c').get_supported_arguments(
+        # Enable GNU features of our dependencies. See feature_test_macros(7).
+        '-D_GNU_SOURCE',
+
+        # Prevent CLANG from complaining that alignof-expressions are GNU-only.
+        '-Wno-gnu-alignof-expression',
+        # Never complain about *MAYBE* uninit variables. This is very flaky and
+        # produces bogus results with LTO.
+        '-Wno-maybe-uninitialized',
+        # Do not complain about unknown GCC/CLANG warnings.
+        '-Wno-unknown-warning-option',
+        # There is no standardized way to mark unused arguments, so never
+        # complain about them.
+        '-Wno-unused-parameter',
+
+        # Preprocessor evaluations often lead to warnings about comparisons
+        # that are always true/false. Make sure they do not break a build but
+        # keep them on for diagnostics.
+        '-Wno-error=type-limits',
+        # As we use designated field-initializers, this warning should never
+        # trigger, but still does on GCC in combination with some other
+        # preprocessor checks. Lets just make sure it does not break builds.
+        '-Wno-error=missing-field-initializers',
+
+        # Warn if we ever use `__DATE__` and similar in our build. We want
+        # reproducible builds.
+        '-Wdate-time',
+        # We strictly follow decl-before-statements, so check it.
+        '-Wdeclaration-after-statement',
+        # More strict logical-op sanity checks.
+        '-Wlogical-op',
+        # Loudly complain about missing include-directories.
+        '-Wmissing-include-dirs',
+        # We want hints about noreturn functions, so warn about them.
+        '-Wmissing-noreturn',
+        # Warn if an extern-decl is inside a function. We want imports as
+        # global attributes, never as local ones.
+        '-Wnested-externs',
+        # Warn about redundant declarations. We want declarations in headers
+        # and want them to be unique.
+        '-Wredundant-decls',
+        # Warn about shadowed variables so we do not accidentally override
+        # variables of parent scopes and thus confuse macros.
+        '-Wshadow',
+        # Warn about aliasing violations. Level-3 produces the least false
+        # positives, but is the slowest. Force it to avoid breaking -Werror
+        # builds.
+        '-Wstrict-aliasing=3',
+        # Suggest 'noreturn' attributes. They are useful, we want them!
+        '-Wsuggest-attribute=noreturn',
+        # Warn about undefined identifiers in preprocessor conditionals.
+        '-Wundef',
+        # Make sure literal strings are considered 'const'.
+        '-Wwrite-strings',
+)
+assert(not ''.join(cflags).contains(' '), 'Malformed compiler flags.')
+add_project_arguments(cflags, language: 'c')
+
+#
+# Version Scripts
+#
+
+use_version_scripts = get_option('version-scripts')
+if use_version_scripts == 'auto'
+        use_version_scripts = meson.get_compiler('c').has_link_argument(
+                '-Wl,--version-script=' + (meson.current_source_dir() / 'src/libcstdaux.sym')
+        ) ? 'yes' : 'no'
+endif
+
+#
+# Subdir Delegation
+#
+
+subdir('src')
+
+#
+# Meson Subproject Configuration
+#
+
+meson.override_dependency('libcstdaux-'+major, libcstdaux_dep, static: true)
diff --git a/src/c-stdaux/meson_options.txt b/src/c-stdaux/meson_options.txt
new file mode 100644
index 00000000..82fdaf78
--- /dev/null
+++ b/src/c-stdaux/meson_options.txt
@@ -0,0 +1,7 @@
+option(
+        'version-scripts',
+        choices: ['yes', 'no', 'auto'],
+        description: 'Enable GNU-version-scripts for linking',
+        type: 'combo',
+        value: 'auto',
+)
diff --git a/src/c-stdaux/src/docs/api.rst b/src/c-stdaux/src/docs/api.rst
new file mode 100644
index 00000000..a25e9fad
--- /dev/null
+++ b/src/c-stdaux/src/docs/api.rst
@@ -0,0 +1,5 @@
+API
+===
+
+.. c:autodoc:: c-stdaux.h c-stdaux-generic.h c-stdaux-gnuc.h c-stdaux-unix.h
+   :transform: kerneldoc
diff --git a/src/c-stdaux/src/docs/conf.py b/src/c-stdaux/src/docs/conf.py
new file mode 100644
index 00000000..b036efa5
--- /dev/null
+++ b/src/c-stdaux/src/docs/conf.py
@@ -0,0 +1,45 @@
+#
+# Sphinx Documentation Configuration
+#
+
+import re
+import os
+import sys
+
+import capidocs.kerneldoc
+import hawkmoth
+
+# Global Setup
+
+project = 'c-stdaux'
+
+author = 'C-Util Community'
+copyright = '2022, C-Util Community'
+
+# Hawkmoth C-Audodoc Setup
+
+capidocs.kerneldoc.hawkmoth_conf()
+
+# Extensions
+
+exclude_patterns = []
+
+extensions = [
+    'hawkmoth',
+]
+
+# Hawkmoth Options
+
+cautodoc_clang = capidocs.kerneldoc.hawkmoth_include_args()
+cautodoc_clang += ["-I" + os.path.abspath("..")]
+cautodoc_clang += ["-DC_COMPILER_DOCS"]
+
+cautodoc_root = os.path.abspath('..')
+
+cautodoc_transformations = {
+    'kerneldoc': capidocs.kerneldoc.hawkmoth_converter,
+}
+
+# HTML Options
+
+html_theme = 'sphinx_rtd_theme'
diff --git a/src/c-stdaux/src/docs/index.rst b/src/c-stdaux/src/docs/index.rst
new file mode 100644
index 00000000..5ad99374
--- /dev/null
+++ b/src/c-stdaux/src/docs/index.rst
@@ -0,0 +1,14 @@
+Introduction
+============
+
+The **c-stdaux** project contains support-macros and auxiliary functions around
+the functionality of common C standard libraries. This includes helpers for the
+ISO-C Standard Library, but also other common specifications like POSIX or
+common extended features of wide-spread compilers like gcc and clang.
+
+.. toctree::
+   :caption: Library Documentation
+   :hidden:
+
+   self
+   api
diff --git a/src/c-stdaux/src/docs/requirements.txt b/src/c-stdaux/src/docs/requirements.txt
new file mode 100644
index 00000000..97872db1
--- /dev/null
+++ b/src/c-stdaux/src/docs/requirements.txt
@@ -0,0 +1,3 @@
+c-apidocs>=0.0.2
+clang>=6
+hawkmoth>=0.7
diff --git a/src/c-stdaux/src/libcstdaux.sym b/src/c-stdaux/src/libcstdaux.sym
new file mode 100644
index 00000000..2073665f
--- /dev/null
+++ b/src/c-stdaux/src/libcstdaux.sym
@@ -0,0 +1,6 @@
+LIBCSTDAUX_1 {
+global:
+        c_internal_dummy;
+local:
+       *;
+};
diff --git a/src/c-stdaux/src/meson.build b/src/c-stdaux/src/meson.build
new file mode 100644
index 00000000..f0efa90b
--- /dev/null
+++ b/src/c-stdaux/src/meson.build
@@ -0,0 +1,43 @@
+#
+# target: libcstdaux.so
+# (No .so is built so far, since we are header-only. This might change in the
+#  future, if we add more complex helpers.)
+#
+
+libcstdaux_vars = {
+        'cflags': ' '.join(cflags),
+        'version-scripts': use_version_scripts,
+}
+
+libcstdaux_dep = declare_dependency(
+        include_directories: include_directories('.'),
+        variables: libcstdaux_vars,
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers(
+                'c-stdaux.h',
+                'c-stdaux-generic.h',
+                'c-stdaux-gnuc.h',
+                'c-stdaux-unix.h',
+        )
+
+        mod_pkgconfig.generate(
+                description: project_description,
+                filebase: 'libcstdaux-'+major,
+                name: 'libcstdaux',
+                unescaped_variables: libcstdaux_vars,
+                version: meson.project_version(),
+        )
+endif
+
+#
+# target: test-*
+#
+
+test_api = executable('test-api', ['test-api.c'], dependencies: libcstdaux_dep)
+test('API Symbol Visibility', test_api)
+
+test_basic = executable('test-basic', ['test-basic.c'], dependencies: libcstdaux_dep)
+test('Basic API Behavior', test_basic)
diff --git a/src/c-stdaux/src/test-api.c b/src/c-stdaux/src/test-api.c
new file mode 100644
index 00000000..e52d42c2
--- /dev/null
+++ b/src/c-stdaux/src/test-api.c
@@ -0,0 +1,322 @@
+/*
+ * API Visibility Tests
+ * This verifies the visibility and availability of the exported API.
+ */
+
+#undef NDEBUG
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "c-stdaux.h"
+
+#if defined(C_MODULE_GENERIC)
+
+static inline _c_always_inline_ int always_inline_fn(void) { return 0; }
+_c_public_ int c_internal_public_fn(void);
+_c_public_ int c_internal_public_fn(void) { return 0; }
+
+static void cleanup_fn(int p) { (void)p; }
+static void direct_cleanup_fn(int p) { (void)p; }
+C_DEFINE_CLEANUP(int, cleanup_fn);
+C_DEFINE_DIRECT_CLEANUP(int, direct_cleanup_fn);
+
+static void test_api_generic(void) {
+        /* C_COMPILER_* */
+        {
+#ifdef __clang__
+                c_assert(C_COMPILER_CLANG);
+#endif
+#ifdef __GNUC__
+                c_assert(C_COMPILER_GNUC);
+#endif
+#ifdef _MSC_VER
+                c_assert(C_COMPILER_MSVC);
+#endif
+        }
+
+        /* C_OS_* */
+        {
+#ifdef __linux__
+                c_assert(C_OS_LINUX);
+#endif
+#ifdef __APPLE__
+                c_assert(C_OS_MACOS);
+#endif
+#ifdef _WIN32
+                c_assert(C_OS_WINDOWS);
+#endif
+        }
+
+        /* _c_always_inline_ */
+        {
+                c_assert(!always_inline_fn());
+        }
+
+        /* _c_boolean_expr_ */
+        {
+                c_assert(_c_boolean_expr_(true));
+        }
+
+        /* _c_likely_ */
+        {
+                c_assert(_c_likely_(true));
+        }
+
+        /* _c_public_ */
+        {
+                c_assert(!c_internal_public_fn());
+        }
+
+        /* _c_unlikely_ */
+        {
+                c_assert(!_c_unlikely_(false));
+        }
+
+        /* C_STRINGIFY */
+        {
+                const char v[] = C_STRINGIFY(foobar);
+
+                c_assert(!strcmp(v, "foobar"));
+        }
+
+        /* C_CONCATENATE */
+        {
+                int C_CONCATENATE(a, b) = 0;
+
+                c_assert(!ab);
+        }
+
+        /* C_EXPAND */
+        {
+                int x[] = { C_EXPAND((0, 1)) };
+
+                c_assert(sizeof(x) / sizeof(*x) == 2);
+        }
+
+        /* C_VAR */
+        {
+                int C_VAR = 0; c_assert(!C_VAR); /* must be on the same line */
+        }
+
+        /* c_assume_aligned */
+        {
+                _Alignas(16) uint8_t data[8] = { 0 };
+
+                c_assert(c_assume_aligned(data, 16, 0));
+        }
+
+        /* c_assert */
+        {
+                c_assert(true);
+        }
+
+        /* c_load */
+        {
+                uint64_t data[128] = { 0 };
+
+                c_assert(c_load(uint64_t, le, aligned, data, 0) == 0);
+        }
+
+        /* C_DEFINE_CLEANUP / C_DEFINE_DIRECT_CLEANUP */
+        {
+                int v = 0;
+
+                cleanup_fnp(&v);
+                direct_cleanup_fnp(&v);
+        }
+
+        /* test availability of C symbols */
+        {
+                void *fns[] = {
+                        (void *)c_errno,
+                        (void *)c_memset,
+                        (void *)c_memzero,
+                        (void *)c_memcpy,
+                        (void *)c_load_8,
+                        (void *)c_load_16be_unaligned,
+                        (void *)c_load_16be_aligned,
+                        (void *)c_load_16le_unaligned,
+                        (void *)c_load_16le_aligned,
+                        (void *)c_load_32be_unaligned,
+                        (void *)c_load_32be_aligned,
+                        (void *)c_load_32le_unaligned,
+                        (void *)c_load_32le_aligned,
+                        (void *)c_load_64be_unaligned,
+                        (void *)c_load_64be_aligned,
+                        (void *)c_load_64le_unaligned,
+                        (void *)c_load_64le_aligned,
+                        (void *)c_free,
+                        (void *)c_fclose,
+                        (void *)c_freep,
+                        (void *)c_fclosep,
+                };
+                size_t i;
+
+                for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
+                        c_assert(!!fns[i]);
+        }
+}
+
+#else /* C_MODULE_GENERIC */
+
+static void test_api_generic(void) {
+}
+
+#endif /* C_MODULE_GENERIC */
+
+#if defined(C_MODULE_GNUC)
+
+static _c_const_ int const_fn(void) { return 0; }
+static _c_deprecated_ _c_unused_ int deprecated_fn(void) { return 0; }
+_c_hidden_ int c_internal_hidden_fn(void);
+_c_hidden_ int c_internal_hidden_fn(void) { return 0; }
+static _c_printf_(1, 2) int printf_fn(const _c_unused_ char *f, ...) { return 0; }
+static _c_pure_ int pure_fn(void) { return 0; }
+static _c_sentinel_ int sentinel_fn(const _c_unused_ char *f, ...) { return 0; }
+static _c_unused_ int unused_fn(void) { return 0; }
+
+static void test_api_gnuc(void) {
+        /* _c_cleanup_ */
+        {
+                _c_cleanup_(c_freep) void *foo = NULL;
+                c_assert(!foo);
+        }
+
+        /* _c_const_ */
+        {
+                c_assert(!const_fn());
+        }
+
+        /* _c_deprecated_ */
+        {
+                /* see deprecated_fn() */
+        }
+
+        /* _c_hidden_ */
+        {
+                c_assert(!c_internal_hidden_fn());
+        }
+
+        /* _c_packed_ */
+        {
+                struct _c_packed_ FooBar {
+                        int member;
+                } foobar = {};
+
+                c_assert(!foobar.member);
+        }
+
+        /* _c_printf_ */
+        {
+                c_assert(!printf_fn("%d", 1));
+        }
+
+        /* _c_pure_ */
+        {
+                c_assert(!pure_fn());
+        }
+
+        /* _c_sentinel_ */
+        {
+                c_assert(!sentinel_fn("", NULL));
+        }
+
+        /* _c_unused_ */
+        {
+                c_assert(!unused_fn());
+        }
+
+        /* C_EXPR_ASSERT */
+        {
+                int v = C_EXPR_ASSERT(0, true, "");
+
+                c_assert(!v);
+        }
+
+        /* C_CC_MACRO1, C_CC_MACRO2, C_CC_MACRO3 */
+        {
+#define MACRO_REAL(_x1, _x2, _x3) ((_x1 + _x2 + _x3) * 0)
+#define MACRO1(_x1) C_CC_MACRO1(MACRO_REAL, _x1, 0, 0)
+#define MACRO2(_x1, _x2) C_CC_MACRO2(MACRO_REAL, _x1, _x2, 0)
+#define MACRO3(_x1, _x2, _x3) C_CC_MACRO3(MACRO_REAL, _x1, _x2, _x3)
+                c_assert(!MACRO1(1));
+                c_assert(!MACRO2(1, 1));
+                c_assert(!MACRO3(1, 1, 1));
+#undef MACRO3
+#undef MACRO2
+#undef MACRO1
+        }
+
+        /* C_ARRAY_SIZE */
+        {
+                int v[] = { 0, 1, 2 };
+                c_assert(C_ARRAY_SIZE(v) == 3);
+        }
+
+        /* C_DECIMAL_MAX */
+        {
+                c_assert(C_DECIMAL_MAX(uint8_t) == 4);
+        }
+
+        /* c_container_of */
+        {
+                struct FooBarContainer {
+                        int member;
+                } v = {};
+
+                c_assert(c_container_of(&v.member, struct FooBarContainer, member) == &v);
+        }
+
+        /* c_max, c_min, c_less_by, c_clamp, c_div_round_up */
+        {
+                c_assert(c_max(0, 0) == 0);
+                c_assert(c_min(0, 0) == 0);
+                c_assert(c_less_by(0, 0) == 0);
+                c_assert(c_clamp(0, 0, 0) == 0);
+                c_assert(c_div_round_up(1, 1) == 1);
+        }
+
+        /* c_align_to */
+        {
+                c_assert(c_align_to(0, 0) == 0);
+        }
+}
+
+#else /* C_MODULE_GNUC */
+
+static void test_api_gnuc(void) {
+}
+
+#endif /* C_MODULE_GNUC */
+
+#if defined(C_MODULE_UNIX)
+
+static void test_api_unix(void) {
+        /* test availability of C symbols */
+        {
+                void *fns[] = {
+                        (void *)c_close,
+                        (void *)c_closedir,
+                        (void *)c_closep,
+                        (void *)c_closedirp,
+                };
+                size_t i;
+
+                for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
+                        c_assert(!!fns[i]);
+        }
+}
+
+#else /* C_MODULE_UNIX */
+
+static void test_api_unix(void) {
+}
+
+#endif /* C_MODULE_UNIX */
+
+int main(void) {
+        test_api_generic();
+        test_api_gnuc();
+        test_api_unix();
+        return 0;
+}
diff --git a/src/c-stdaux/src/test-basic.c b/src/c-stdaux/src/test-basic.c
new file mode 100644
index 00000000..1d16a828
--- /dev/null
+++ b/src/c-stdaux/src/test-basic.c
@@ -0,0 +1,624 @@
+/*
+ * Tests for Basic Functionality
+ *
+ * This runs same basic verification that each feature does what we expect it
+ * to do. More elaborate tests and/or stress-tests are not included here.
+ */
+
+#undef NDEBUG
+#include <stdlib.h>
+#include "c-stdaux.h"
+
+#if defined(C_MODULE_GENERIC)
+
+static int check_cassert_unreachable(int switch_val) {
+    int result;
+
+    /* Check whether this triggers a "-Wsometimes-uninitialized" warning or
+     * whether the compiler recognizes c_assert(0) as unreachable code. */
+    switch (switch_val) {
+    case 1: result = 1; break;
+    case 2: result = 2; break;
+    default: c_assert(0);
+    }
+
+    return result;
+}
+
+static void test_basic_generic(int non_constant_expr) {
+        /*
+         * Verify `_c_boolean_expr_` evaluates expressions to a boolean value
+         * and correctly works on all platforms.
+         */
+        {
+                int v = 0;
+
+                c_assert(_c_boolean_expr_(0) == 0);
+                c_assert(_c_boolean_expr_(1) == 1);
+                c_assert(_c_boolean_expr_(2) == 1);
+                c_assert(_c_boolean_expr_(INT_MIN) == 1);
+                c_assert(_c_boolean_expr_(INT_MAX) == 1);
+
+                /* verify no double-evaluation takes place */
+                c_assert(_c_boolean_expr_(v++) == 0);
+                c_assert(_c_boolean_expr_(v) == 1);
+
+#if defined(C_COMPILER_GNUC)
+                c_assert(__builtin_constant_p(_c_boolean_expr_(1)));
+                c_assert(!__builtin_constant_p(_c_boolean_expr_(non_constant_expr)));
+#endif
+        }
+
+        /*
+         * Test that _c_likely_() and _c_unlikely_() can deal with constant
+         * expressions.
+         */
+        {
+#if defined(C_COMPILER_GNUC)
+                c_assert(__builtin_constant_p(_c_likely_(1)));
+                c_assert(__builtin_constant_p(_c_unlikely_(1)));
+                c_assert(!__builtin_constant_p(_c_likely_(non_constant_expr)));
+                c_assert(!__builtin_constant_p(_c_unlikely_(non_constant_expr)));
+#endif
+        }
+
+        /*
+         * Test stringify/concatenation helpers. Also make sure to test that
+         * the passed arguments are evaluated first, before they're stringified
+         * and/or concatenated.
+         */
+        {
+#define TEST_TOKEN foobar
+                c_assert(!strcmp("foobar", C_STRINGIFY(foobar)));
+                c_assert(!strcmp("foobar", C_STRINGIFY(TEST_TOKEN)));
+                c_assert(!strcmp("foobar", C_STRINGIFY(C_CONCATENATE(foo, bar))));
+                c_assert(!strcmp("foobarfoobar", C_STRINGIFY(C_CONCATENATE(TEST_TOKEN, foobar))));
+                c_assert(!strcmp("foobarfoobar", C_STRINGIFY(C_CONCATENATE(foobar, TEST_TOKEN))));
+#undef TEST_TOKEN
+        }
+
+        /*
+         * Test tuple expansion. This is used to strip tuple-wrappers in the
+         * pre-processor.
+         * We make sure that it works with {0,1,2}-tuples, as well as only
+         * strips a single layer.
+         */
+        {
+                /*
+                 * strcmp() might be a macro, so make sure we get a proper C
+                 * expression below. Otherwise, C_EXPAND() cannot be used that
+                 * way (since it would evaluate to a single macro argument).
+                 */
+                int (*f) (const char *, const char *) = strcmp;
+
+                c_assert(!f(C_EXPAND(()) "foobar", "foo" "bar"));
+                c_assert(!f(C_EXPAND(("foobar")), "foo" "bar"));
+                c_assert(!f(C_EXPAND(("foobar", "foo" "bar"))));
+                c_assert(!f C_EXPAND((("foobar", "foo" "bar"))));
+        }
+
+        /*
+         * Test C_VAR() macro. It's sole purpose is to create a valid C
+         * identifier given a single argument (which itself must be a valid
+         * identifier).
+         * Just test that we can declare variables with it and use it in
+         * expressions.
+         */
+        {
+                {
+                        int C_VAR(sub, UNIQUE) = 5;
+                        /* make sure the variable name does not clash */
+                        int sub = 12, subUNIQUE = 12, UNIQUEsub = 12;
+
+                        c_assert(7 + C_VAR(sub, UNIQUE) == sub);
+                        c_assert(sub == subUNIQUE);
+                        c_assert(sub == UNIQUEsub);
+                }
+                {
+                        /* verify C_VAR() with single argument works line-based */
+                        int C_VAR(sub); C_VAR(sub) = 5; c_assert(C_VAR(sub) == 5);
+                }
+                {
+                        /* verify C_VAR() with no argument works line-based */
+                        int C_VAR(); C_VAR() = 5; c_assert(C_VAR() == 5);
+                }
+#if defined(C_MODULE_GNUC)
+                {
+                        /*
+                         * Make sure both produce different names, even though they're
+                         * exactly the same expression.
+                         */
+                        _c_unused_ int C_VAR(sub, __COUNTER__), C_VAR(sub, __COUNTER__);
+                }
+#endif
+        }
+
+#if defined(C_MODULE_GNUC)
+        /*
+         * Verify that c_free*() works as expected. Since we want to support
+         * running under valgrind, there is no easy way to verify the
+         * correctness of free(). Hence, we simply rely on valgrind to catch
+         * the leaks.
+         */
+        {
+                int i;
+
+                for (i = 0; i < 16; ++i) {
+                        _c_cleanup_(c_freep) void *foo;
+                        _c_cleanup_(c_freep) int **bar; /* supports any type */
+                        size_t sz = 128 * 1024;
+
+                        foo = malloc(sz);
+                        c_assert(foo);
+
+                        bar = malloc(sz);
+                        c_assert(bar);
+                        bar = c_free(bar);
+                        c_assert(!bar);
+                }
+
+                c_assert(c_free(NULL) == NULL);
+        }
+#endif
+
+#if defined(C_MODULE_UNIX)
+        /*
+         * Test c_fclose() and c_fclosep(). This uses the same logic as the
+         * tests for c_close() (i.e., sparse FD allocation).
+         */
+        {
+                int r, i, fd, tmp[2];
+                FILE *f;
+
+                r = pipe(tmp);
+                c_assert(r >= 0);
+                fd = tmp[0];
+                c_close(tmp[1]);
+
+                f = fdopen(fd, "r");
+                c_assert(f);
+
+                /* verify c_fclose() returns NULL */
+                f = c_fclose(f);
+                c_assert(!f);
+
+                /* verify c_fclose() deals fine with NULL */
+                c_assert(!c_fclose(NULL));
+
+                /* make sure c_flosep() deals fine with NULL */
+                {
+                        _c_cleanup_(c_fclosep) _c_unused_ FILE *t = (void *)0xdeadbeef;
+                        t = NULL;
+                }
+
+                /*
+                 * Make sure the c_fclose() earlier worked, by allocating the
+                 * FD again and relying on the same FD number to be reused. Do
+                 * this twice, to verify that the c_fclosep() in the cleanup
+                 * path works as well.
+                 */
+                for (i = 0; i < 2; ++i) {
+                        _c_cleanup_(c_fclosep) _c_unused_ FILE *t = NULL;
+                        int tfd;
+
+                        r = pipe(tmp);
+                        c_assert(r >= 0);
+                        tfd = tmp[0];
+                        c_close(tmp[1]);
+
+                        c_assert(tfd == fd); /* the same as before */
+                        t = fdopen(tfd, "r");
+                        c_assert(t);
+                }
+        }
+#endif
+
+        /*
+         * Test c_assert(). Make sure side-effects are always evaluated, and
+         * variables are marked as used regardless of NDEBUG.
+         */
+        {
+                int v1 = 0, v2 = 0;
+
+#define NDEBUG 1
+                c_assert(!v1);
+                if (v1)
+                        abort();
+                c_assert(++v1);
+                if (v1 != 1)
+                        abort();
+#undef NDEBUG
+                c_assert(!v2);
+                if (v2)
+                        abort();
+                c_assert(++v2);
+                if (v2 != 1)
+                        abort();
+
+                /*
+                 * Use the `check_cassert_unreachable()` helper to verify the
+                 * compiler does not complain about unreachable code when
+                 * `c_assert(0)` is used.
+                 */
+                c_assert(check_cassert_unreachable(1) == 1);
+                c_assert(check_cassert_unreachable(2) == 2);
+        }
+
+        /*
+         * Test c_errno(). Simply verify that the correct value is returned. It
+         * must always be >0 and equivalent to `errno' if set.
+         */
+        {
+                c_assert(c_errno() > 0);
+
+                strtol("0xfffffffffffffffffffffffffffffffff", NULL, 0);
+                c_assert(errno == ERANGE);
+                c_assert(c_errno() == errno);
+
+                errno = 0;
+                c_assert(c_errno() != errno);
+        }
+
+        /*
+         * Test c_memset(). Simply verify its most basic behavior, as well as
+         * calling it on empty regions.
+         */
+        {
+                uint64_t v = (uint64_t)-1;
+                size_t n;
+                void *p;
+
+                /* try filling with 0 and 0xff */
+                c_assert(v == (uint64_t)-1);
+                c_memset(&v, 0, sizeof(v));
+                c_assert(v == (uint64_t)0);
+                c_memset(&v, 0xff, sizeof(v));
+                c_assert(v == (uint64_t)-1);
+
+                /*
+                 * Try tricking the optimizer into thinking @p cannot be NULL,
+                 * as normal `memset(3)` would allow.
+                 */
+                p = NULL;
+                n = 0;
+                c_memset(p, 0, n);
+                if (p)
+                        abort();
+                c_assert(p == NULL);
+        }
+
+        /*
+         * Test c_memzero(). Simply verify it can clear a trivial area to 0.
+         */
+        {
+                uint64_t v = (uint64_t)-1;
+
+                c_assert(v == (uint64_t)-1);
+                c_memzero(&v, sizeof(v));
+                c_assert(v == (uint64_t)0);
+        }
+
+        /*
+         * Test c_memcpy() with a simple 8-byte copy.
+         */
+        {
+                uint64_t v1 = (uint64_t)-1, v2 = (uint64_t)0;
+
+                c_assert(v1 == (uint64_t)-1);
+                c_memcpy(&v1, &v2, sizeof(v1));
+                c_assert(v1 == (uint64_t)0);
+
+                c_memcpy(NULL, NULL, 0);
+        }
+
+        /*
+         * Test c_memcmp() with.
+         */
+        {
+                uint64_t v1 = (uint64_t)-1, v2 = (uint64_t)0;
+
+                c_assert(c_memcmp(NULL, NULL, 0) == 0);
+                c_assert(c_memcmp(&v1, &v2, 0) == 0);
+                c_assert(c_memcmp(&v1, &v2, 8) != 0);
+        }
+
+        /*
+         * Test c_load*() and its mapping to c_load_*() functions.
+         */
+        {
+                _Alignas(8) uint8_t data[16] = {
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        1, 2, 3, 4,
+                        5, 6, 7, 8,
+                };
+
+                c_assert(c_load_8(data, 7) == 0);
+                c_assert(c_load_8(data, 8) == 1);
+                c_assert(c_load(uint16_t, be, unaligned, data, 7) == UINT16_C(0x0001));
+                c_assert(c_load(uint16_t, be, aligned, data, 8) == UINT16_C(0x0102));
+                c_assert(c_load(uint16_t, le, unaligned, data, 7) == UINT16_C(0x0100));
+                c_assert(c_load(uint16_t, le, aligned, data, 8) == UINT16_C(0x0201));
+                c_assert(c_load(uint32_t, be, unaligned, data, 7) == UINT32_C(0x00010203));
+                c_assert(c_load(uint32_t, be, aligned, data, 8) == UINT32_C(0x01020304));
+                c_assert(c_load(uint32_t, le, unaligned, data, 7) == UINT32_C(0x03020100));
+                c_assert(c_load(uint32_t, le, aligned, data, 8) == UINT32_C(0x04030201));
+                c_assert(c_load(uint64_t, be, unaligned, data, 7) == UINT64_C(0x0001020304050607));
+                c_assert(c_load(uint64_t, be, aligned, data, 8) == UINT64_C(0x0102030405060708));
+                c_assert(c_load(uint64_t, le, unaligned, data, 7) == UINT64_C(0x0706050403020100));
+                c_assert(c_load(uint64_t, le, aligned, data, 8) == UINT64_C(0x0807060504030201));
+        }
+}
+
+#else /* C_MODULE_GENERIC */
+
+static void test_basic_generic(int non_constant_expr) {
+        (void)non_constant_expr;
+}
+
+#endif /* C_MODULE_GENERIC */
+
+#if defined(C_MODULE_GNUC)
+
+static void test_basic_gnuc(int non_constant_expr) {
+        /*
+         * Test the C_EXPR_ASSERT() macro to work in static and non-static
+         * environments, and evaluate exactly to its passed expression.
+         */
+        {
+                static int v = C_EXPR_ASSERT(1, true, "");
+
+                c_assert(v == 1);
+        }
+
+        /*
+         * Test array-size helper. This simply computes the number of elements
+         * of an array, instead of the binary size.
+         */
+        {
+                int bar[8];
+
+                static_assert(C_ARRAY_SIZE(bar) == 8, "");
+                c_assert(__builtin_constant_p(C_ARRAY_SIZE(bar)));
+        }
+
+        /*
+         * Test decimal-representation calculator. Make sure it is
+         * type-independent and just uses the size of the type to calculate how
+         * many bytes are needed to print that integer in decimal form. Also
+         * verify that it is a constant expression.
+         */
+        {
+                static_assert(C_DECIMAL_MAX(char) == 4, "");
+                static_assert(C_DECIMAL_MAX(signed char) == 4, "");
+                static_assert(C_DECIMAL_MAX(unsigned char) == 4, "");
+                static_assert(C_DECIMAL_MAX(unsigned long) == (sizeof(long) == 8 ? 21 : 11), "");
+                static_assert(C_DECIMAL_MAX(unsigned long long) == 21, "");
+                static_assert(C_DECIMAL_MAX(int32_t) == 11, "");
+                static_assert(C_DECIMAL_MAX(uint32_t) == 11, "");
+                static_assert(C_DECIMAL_MAX(uint64_t) == 21, "");
+        }
+
+        /*
+         * Test c_container_of(). We cannot test for type-safety, nor for
+         * other invalid uses, as they'd require negative compile-testing.
+         * However, we can test that the macro yields the correct values under
+         * normal use.
+         */
+        {
+                struct foobar {
+                        int a;
+                        char b;
+                } sub = {};
+
+                c_assert(&sub == c_container_of(&sub.a, struct foobar, a));
+                c_assert(&sub == c_container_of(&sub.b, struct foobar, b));
+                c_assert(&sub == c_container_of((const char *)&sub.b, struct foobar, b));
+
+                c_assert(!c_container_of(NULL, struct foobar, b));
+        }
+
+        /*
+         * Test min/max macros. Especially check that macro arguments are never
+         * evaluated multiple times, and if both arguments are constant, the
+         * return value is constant as well.
+         */
+        {
+                int foo;
+
+                foo = 0;
+                c_assert(c_max(1, 5) == 5);
+                c_assert(c_max(-1, 5) == 5);
+                c_assert(c_max(-1, -5) == -1);
+                c_assert(c_max(foo++, -1) == 0);
+                c_assert(foo == 1);
+                c_assert(c_max(foo++, foo++) > 0);
+                c_assert(foo == 3);
+
+                c_assert(__builtin_constant_p(c_max(1, 5)));
+                c_assert(!__builtin_constant_p(c_max(1, non_constant_expr)));
+
+                foo = 0;
+                c_assert(c_min(1, 5) == 1);
+                c_assert(c_min(-1, 5) == -1);
+                c_assert(c_min(-1, -5) == -5);
+                c_assert(c_min(foo++, 1) == 0);
+                c_assert(foo == 1);
+                c_assert(c_min(foo++, foo++) > 0);
+                c_assert(foo == 3);
+
+                c_assert(__builtin_constant_p(c_min(1, 5)));
+                c_assert(!__builtin_constant_p(c_min(1, non_constant_expr)));
+        }
+
+        /*
+         * Test c_less_by(), c_clamp(). Make sure they
+         * evaluate arguments exactly once, and yield a constant expression,
+         * if all arguments are constant.
+         */
+        {
+                int foo;
+
+                foo = 8;
+                c_assert(c_less_by(1, 5) == 0);
+                c_assert(c_less_by(5, 1) == 4);
+                c_assert(c_less_by(foo++, 1) == 7);
+                c_assert(foo == 9);
+                c_assert(c_less_by(foo++, foo++) >= 0);
+                c_assert(foo == 11);
+
+                c_assert(__builtin_constant_p(c_less_by(1, 5)));
+                c_assert(!__builtin_constant_p(c_less_by(1, non_constant_expr)));
+
+                foo = 8;
+                c_assert(c_clamp(foo, 1, 5) == 5);
+                c_assert(c_clamp(foo, 9, 20) == 9);
+                c_assert(c_clamp(foo++, 1, 5) == 5);
+                c_assert(foo == 9);
+                c_assert(c_clamp(foo++, foo++, foo++) >= 0);
+                c_assert(foo == 12);
+
+                c_assert(__builtin_constant_p(c_clamp(0, 1, 5)));
+                c_assert(!__builtin_constant_p(c_clamp(1, 0, non_constant_expr)));
+        }
+
+        /*
+         * Div Round Up: Normal division, but round up to next integer, instead
+         * of clipping. Also verify that it does not suffer from the integer
+         * overflow in the prevalent, alternative implementation:
+         *      [(x + y - 1) / y].
+         */
+        {
+                int i, j, foo;
+
+#define TEST_ALT_DIV(_x, _y) (((_x) + (_y) - 1) / (_y))
+                foo = 8;
+                c_assert(c_div_round_up(0, 5) == 0);
+                c_assert(c_div_round_up(1, 5) == 1);
+                c_assert(c_div_round_up(5, 5) == 1);
+                c_assert(c_div_round_up(6, 5) == 2);
+                c_assert(c_div_round_up(foo++, 1) == 8);
+                c_assert(foo == 9);
+                c_assert(c_div_round_up(foo++, foo++) >= 0);
+                c_assert(foo == 11);
+
+                c_assert(__builtin_constant_p(c_div_round_up(1, 5)));
+                c_assert(!__builtin_constant_p(c_div_round_up(1, non_constant_expr)));
+
+                /* alternative calculation is [(x + y - 1) / y], but it may overflow */
+                for (i = 0; i <= 0xffff; ++i) {
+                        for (j = 1; j <= 0xff; ++j)
+                                c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j));
+                        for (j = 0xff00; j <= 0xffff; ++j)
+                                c_assert(c_div_round_up(i, j) == TEST_ALT_DIV(i, j));
+                }
+
+                /* make sure it doesn't suffer from high overflow */
+                c_assert(UINT32_C(0xfffffffa) % 10 == 0);
+                c_assert(UINT32_C(0xfffffffa) / 10 == UINT32_C(429496729));
+                c_assert(c_div_round_up(UINT32_C(0xfffffffa), 10) == UINT32_C(429496729));
+                c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffa), 10) == 0); /* overflow */
+
+                c_assert(UINT32_C(0xfffffffd) % 10 == 3);
+                c_assert(UINT32_C(0xfffffffd) / 10 == UINT32_C(429496729));
+                c_assert(c_div_round_up(UINT32_C(0xfffffffd), 10) == UINT32_C(429496730));
+                c_assert(TEST_ALT_DIV(UINT32_C(0xfffffffd), 10) == 0);
+#undef TEST_ALT_DIV
+        }
+
+        /*
+         * Align to multiple of: Test the alignment macro. Check that it does
+         * not suffer from incorrect integer overflows, neither should it
+         * exceed the boundaries of the input type.
+         */
+        {
+                c_assert(c_align_to(UINT32_C(0), 1) == 0);
+                c_assert(c_align_to(UINT32_C(0), 2) == 0);
+                c_assert(c_align_to(UINT32_C(0), 4) == 0);
+                c_assert(c_align_to(UINT32_C(0), 8) == 0);
+                c_assert(c_align_to(UINT32_C(1), 8) == 8);
+
+                c_assert(c_align_to(UINT32_C(0xffffffff), 8) == 0);
+                c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8);
+                c_assert(c_align_to(UINT32_C(0xfffffff1), 8) == 0xfffffff8);
+
+                c_assert(__builtin_constant_p(c_align_to(16, 8)));
+                c_assert(!__builtin_constant_p(c_align_to(non_constant_expr, 8)));
+                c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr)));
+                c_assert(!__builtin_constant_p(c_align_to(16, non_constant_expr ? 8 : 16)));
+                c_assert(__builtin_constant_p(c_align_to(16, 7 + 1)));
+                c_assert(c_align_to(15, non_constant_expr ? 8 : 16) == 16);
+        }
+}
+
+#else /* C_MODULE_GNUC */
+
+static void test_basic_gnuc(int unused0) {
+        (void)unused0;
+}
+
+#endif /* C_MODULE_GNUC */
+
+#if defined(C_MODULE_UNIX)
+
+static void test_basic_unix(void) {
+        /*
+         * Test c_close*(), rely on sparse FD allocation. Make sure all the
+         * helpers actually close the fd, and cope fine with negative numbers.
+         */
+        {
+                int r, i, fd1, fd2, tmp[2];
+
+                r = pipe(tmp);
+                c_assert(r >= 0);
+                fd1 = tmp[0];
+                fd2 = tmp[1];
+
+                /* verify c_close() returns -1 */
+                c_assert(c_close(fd1) == -1);
+                c_assert(c_close(fd2) == -1);
+
+                /* verify c_close() deals fine with negative fds */
+                c_assert(c_close(-1) == -1);
+                c_assert(c_close(-16) == -1);
+
+                /* make sure c_closep() deals fine with negative FDs */
+                {
+                        _c_cleanup_(c_closep) _c_unused_ int t = 0;
+                        t = -1;
+                }
+
+                /*
+                 * Make sure the c_close() earlier worked, by allocating the
+                 * FD again and relying on the same FD number to be reused. Do
+                 * this twice, to verify that the c_closep() in the cleanup
+                 * path works as well.
+                 */
+                for (i = 0; i < 2; ++i) {
+                        _c_cleanup_(c_closep) _c_unused_ int t1 = -1, t2 = -1;
+
+                        r = pipe(tmp);
+                        c_assert(r >= 0);
+                        t1 = tmp[0];
+                        t2 = tmp[1];
+
+                        c_assert(t1 == fd1);
+                        c_assert(t2 == fd2);
+                }
+        }
+}
+
+#else /* C_MODULE_UNIX */
+
+static void test_basic_unix(void) {
+}
+
+#endif /* C_MODULE_UNIX */
+
+int main(int argc, char **argv) {
+        (void)argv;
+        test_basic_generic(argc);
+        test_basic_gnuc(argc);
+        test_basic_unix();
+        return 0;
+}
diff --git a/src/contrib/README.md b/src/contrib/README.md
new file mode 100644
index 00000000..81ebaf03
--- /dev/null
+++ b/src/contrib/README.md
@@ -0,0 +1,11 @@
+contrib
+=======
+
+This directories contains miscellaneous helpers.
+This code is not actually used anywhere in our source
+tree (beside unit tests).
+
+The purpose is that a external libnm/NetworkManager user
+can copy+paste these helpers into their source three. This
+code should act as an example but also be directly usable
+by copying.
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c
index cced1717..2f447146 100644
--- a/src/core/NetworkManagerUtils.c
+++ b/src/core/NetworkManagerUtils.c
@@ -702,8 +702,8 @@ check_connection_controller(NMConnection *orig, NMConnection *candidate, GHashTa
 
     s_con_orig      = nm_connection_get_setting_connection(orig);
     s_con_cand      = nm_connection_get_setting_connection(candidate);
-    orig_controller = nm_setting_connection_get_master(s_con_orig);
-    cand_controller = nm_setting_connection_get_master(s_con_cand);
+    orig_controller = nm_setting_connection_get_controller(s_con_orig);
+    cand_controller = nm_setting_connection_get_controller(s_con_cand);
 
     /* A generated connection uses the UUID to specify the controller. Accept
      * candidates that specify as controller an interface name matching that
diff --git a/src/core/README.l3cfg.md b/src/core/README.l3cfg.md
new file mode 100644
index 00000000..901d40b4
--- /dev/null
+++ b/src/core/README.l3cfg.md
@@ -0,0 +1,368 @@
+L3Cfg Rework
+============
+
+NMDevice is complex. Together with NMManager, NMDevice does too much.
+
+The goal is to rework the IP configuration (Layer 3) to be a more separate
+part of the code that is better maintainable, easier to understand and
+extend and more correct.
+
+Current Situation
+-----------------
+
+- [NMManager](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-manager.c):
+  this is the main object (a singleton) that drives most things.
+  Among many other things, it creates NMDevice instances and coordinates.
+
+- [NMDevice](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c):
+  this represents a device. This is a subclass of NMDBusObject,
+  it is thus directly exported on D-Bus (as D-Bus objects like
+  `/org/freedesktop/NetworkManager/Devices/1`).
+  It also manages all aspects of the device. It has an overall state
+  (`NM_DEVICE_STATE`) but lots of more specific states (e.g. current state
+  of DHCP configuration). As always, the hardest part in programming are
+  stateful objects, and NMDevice has *a lot* of state. The code is huge and
+  hard to understand and the class has (too) many responsibilities. \
+  \
+  NMDevice also has subclasses, which are determined based on the "device type". That
+  means, there are subclasses like NMDeviceEthernet and NMDeviceBridge. As such, the
+  subclasses also export additional D-Bus interfaces. These subclasses also handle
+  the Layer 2 specific aspects of the device. For this aspect, delegation probably
+  would have been a better choice. On the other hand, IP configuration is almost entirely
+  managed by the parent class. Which is good, because the IP configuration is common to all
+  device types, but is bad because NMDevice already does so many things.
+
+- [NMIP4Config](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-ip4-config.c) (and NMIP6Config):
+  these are also subclasses of NMDBusObject
+  and exported on D-Bus on paths like `/org/freedesktop/NetworkManager/IP4Config/1`.
+  The device's `IP4Config` property refers to these objects. They contain
+  the runtime IP information of that device. I don't think these objects
+  should exist on the D-Bus API, as NMDevice could directly expose these properties.
+  But for historic reasons, such is our D-Bus API.
+  Other than that, NMIP4Config objects are also used internally for tracking
+  IP configuration. For example, [when](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dhcp/nm-dhcp-nettools.c#L563)
+  we receive a DHCP lease, we construct a NMIP4Config object with the addresses, DNS settings,
+  and so on. These
+  instances are then [tracked by](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L519)
+  NMDevice, and [merged](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L8928)
+  into an instance that is then exposed on D-Bus. As such, this class has two
+  mostly independent purposes.
+
+- [NMDhcpClient](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dhcp/nm-dhcp-client.c):
+  our DHCP "library". It's a simple object with a clear API that
+  abstracts most of the complexity of handling DHCP. But still, NMDevice
+  needs to drive the DHCP client instance. Meaning, it needs to create (start) and stop
+  them and hook up signals for changes (new lease) and timeout. This is mostly
+  fine and unavoidable. The point is that while specific tasks are well abstracted
+  (like the details of DHCP), there is still some state in NMDevice that is related
+  to manage these tasks. DHCP is one of many such tasks, like also
+  link local addresses, SLAAC or LLDP.
+  This leads to the increased complexity of NMDevice, which manages a large variety
+  of such tasks.
+
+### Problems:
+
+1. first the sheer code size of [nm-device.c](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L19030).
+   It's hard to understand and maintain, and this results in misbehaviours. Also, features that should be easy to implement
+   are not. Also, there are inefficiencies that are hard to fix.
+
+2. NMDevice and NMIP4Config are both exported on D-Bus while having other responsibilities.
+   Being subclasses of NMDBusObject, they are glued to the D-Bus API. For example, NMIP4Config is
+   also used for other purposes (for tracking IP configuration internally).
+
+3. NMDevice simply does too much. IP configuration should be a separate, encapsulated
+   API to make allow NMDevice to be smaller and the IP configuration part better
+   testable, understandable and smaller too.
+
+4. in the current model, NMDevice can be related to zero, one or two ifindexes. For example,
+   for ethernet devices, there is commonly only one actual netdev device (and one ifindex).
+   For OVS devices, there is no ifindex. For NMDeviceModem or NMDeviceBluetooth there is
+   a NMDevice instance that has initially no ifindex (it represents the tty serial port
+   or the bluetooth device) but during activation it gets and ip ifindex. With PPPoE,
+   the ethernet device can even have two ifindexes (one for the underlying ethernet and
+   one for the PPP device). That is all utterly confusing, inconsistent and limited.
+   For example, not all interfaces you see in `ip link` can be found in the D-Bus API.
+   The D-Bus API also does not give access to the ifindex (which is the real identifier
+   for a netdev devices). It only exposes the IpInterface name. That should be improved too,
+   but even such seemingly simple things are not done for years, because it's not trivially
+   clear what the right ifindex is.
+   Also a device instance on D-Bus significantly changes its meaning when it activates/deactivates
+   and it starts/stops being responsible for an ifindex.
+   In the future there should be devices that represent exactly one netdev device (an ifindex)
+   and devices that don't have an ifindex. That is follow up work and hinted by
+   [rhbz#1066703](https://bugzilla.redhat.com/show_bug.cgi?id=1066703). But simplifying
+   the IP configuration is a requisite before addressing that rework.
+   With this we will have controller and controlled devices. That means, a controller devices
+   (that for example represents a bluetooth device) will need to configure IP address on the
+   controlled IP device. That would be doable by injecting the IP config on that device,
+   but as the device already does so much, it would be better if this would be a separate
+   IP configuration manager for that ifindex.
+
+5. NMIP4Config exports properties on D-Bus like [AddressData](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/introspection/org.freedesktop.NetworkManager.IP4Config.xml#L26).
+   which are the currently configured IP addresses. These should be directly obtained
+   from the NMPlatform cache, which contains the correct list of addresses as kernel
+   exposes them via rtnetlink. Instead, whenever there are changes in platform we
+   [generate](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14223)
+   an NMIP4Config instance, then we merge, intersect and subtract this captured information
+   with the IP configs we want to configure. Finally we merge them together again
+   and sync the result to platform. This is bad, wrong and inefficient.
+   We must not mix "what is configured" with "what we want to configure". The current
+   approach also re-generates these IP config instance whenever something in platform changes.
+   That does not scale. If we have any hope to handle thousands of routes, this needs to change.
+
+6. The NMIP4Config objects are mutable, and they are heavily mutated. When we create an NMIP4Config
+   instance that represent a DHCP lease, we will [subtract](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14236)
+   addresses that were externally removed. That is wrong, because during a reapply we
+   will need to know these addresses again. The solution for that is not to mutate this
+   data, but track whether IP addresses are removed separately.
+
+7. NMDevice also does ACD, but it can only do it for addresses received via DHCP.
+   It implicitly also does ACD for IPv4LL, but that is via using the n_ipv4ll library.
+   It would be good to have an option that we can configure IPv4LL for any address.
+   Also, if you manually configure an address like 192.168.2.5 (for which we don't do
+   ACD) and the same address is obtained via DHCP, then doing ACD for the address is wrong.
+   There needs to be link-wide view of the addresses, and not only looking at individual
+   addresses when deciding to do ACD.
+
+8. As IP configuration is done by NMDevice, VPN connections have limited capabilities
+   in this regard.
+   When a VPN has IP addresses, then it injects them into NMDevice by
+   [providing](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L13696)
+   an NMIP4Config. However, that means VPNs cannot do DHCP or IPv4LL, because it can
+   only inject known configuration. That would be very useful for example with a tap
+   device with openvpn. The real problem here is that NMVpnConnection are
+   treated special, when they should be more like devices. That should be reworked in the future,
+   by reworking VPN plugins. Regardless, having IP configuration handled by NMDevice is limiting.
+
+9. NetworkManager currently supports `ipv4.method` which can be "manual", "disabled" or
+   "auto". This scheme does not allow for example to enable IPv4LL together with DHCPv4.
+   As a special case, you can configure `ipv4.method=auto` together with static
+   addresses in `ipv4.addresses`, so combining DHCP and static addressing works. But in general,
+   this scheme is limited. In the future we should have more flexible schemes, where
+   addressing methods can be independently enabled or disabled. Also, we currently
+   have `ipv4.may-fail`, but that is limited as well. For example,
+   `ipv4.may-fail=yes` and `ipv6.may-fail=yes` still means that at least one of the
+   address families must succeed. That makes sense for certain use cases, but it
+   means, you cannot have truly best-effort, opportunistic DHCP with this way.
+   As workaround for that there is `ipv4.dhcp-timeout=infinity`. In
+   general it is not only useful to enable methods independently, we also configure
+   independently whether they are required or optional (and possibly, that they are optional
+   but at least one of several optional methods must succeed). Anyway. The point
+   is there is a need to make IP configuration more flexible. Currently it is not.
+   Such a seemingly simple extension would be surprisingly difficult to implement
+   because [the code](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L6616) is
+   all over the place. The way how NMDevice tracks the overall activation state is
+   hard to understand. This should be improved and possibly could be improved in a
+   smaller refactoring effort. But instead of a smaller effort, we will use the big hammer
+   with L3Cfg rework.
+
+10. There are two classes NMIP4Config and NMIP6Config. Handling both address families is
+   commonly similar, so there is lot of similar code in both. They should be unified
+   so that similar code can handle both address families.
+
+
+Solution and Future
+-------------------
+
+NML3Cfg work is supposed to simplify some part of NMDevice: the part related to
+IP configuration. This is a huge rework of a core part of NetworkManager. Arguably,
+some parts maybe could be done more evolutionary, but the fundamental problems require
+to rip out NMIP4Config and replace it by something better. Doing that is a large rework
+that changes NMDevice heavily. That is also the opportunity to get the smaller issues
+right.
+
+There is already a new class [NML3Cfg](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.h#L141)
+(currently unused). An NML3Cfg instance is responsible for handling IP configuration
+of an ifindex. Consequently, we can ask NMNetns to [get](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-netns.c#L142)
+(or create) a NML3Cfg instance for an ifindex.
+The idea is that there can be multiple users (NMDevice and NMVpnConnection and future controller devices)
+that use the same NML3Cfg instance. Especially with a future rework of NMDevice where
+a NMDevice only manages one ifindex (or none), there is a need that multiple
+devices manage the IP configuration on the same device. Independent users can cooperate
+to configure IP configuration on the same device. Already now with Libreswan VPN, where the VPN "injects"
+its NMIP4Config in NMDevice. Or with PPPoE, where the NMDeviceEthernet is both about IP configuration
+for the PPPoE device.
+
+There is also a new class [NML3ConfigData](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3-config-data.h).
+This replaces some aspect of NMIP4Config/NMIP6Config. A NML3ConfigData object is immutable and has no real logic
+(or state). It has some "logic", like comparing two NML3ConfigData instances, logging it, or merging two (immutable)
+instances into a new instance. But as the class is immutable, that logic is rather simple. This class is
+used to track information. As it's immutable, anybody who is interested can keep a reference
+for it's own purpose. For example, NMDhcpClient will generate a NML3ConfigData with the information
+of the lease. It may keep the reference, but it will also tell NMDevice about it. The NMDevice
+will then itself tell NML3Cfg to accept this configuration. This works by calling
+[add()/remove()](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2654).
+One NML3ConfigData can also track both IPv4 and IPv6 information. It's a general set of IP related
+configuration, that has some address specific properties. Those are then duplicated for both address
+families and implemented in a way to minimize code duplication and encourage to treat them the same.
+As this replaces an aspect of NMIP4Config, NMIP4Config can focus on it's other purpose: to expose data on D-Bus.
+
+What NML3Cfg then does, is to merge all NML3ConfigData, and "commit" it to platform. Thereby it knows
+which addresses it configured the last time (if they no longer are to be configured, they must be removed).
+This is done [here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3442).
+
+As independent users should be able to cooperate, it is not appropriate that they call "commit".
+Instead, they set a commit type ([here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3476),
+and whenever something changes, NML3Cfg knows the aggregated commit type. That is necessary
+because when we activate a device, we may want to preserve the existing IP configuration (e.g. after
+a restart of NetworkManager). During that time is the NML3Cfg instance set to a reduced commit
+mode (ASSUME).
+
+NML3Cfg will also handle IPv4 ACD. Any user of NML3Cfg registers/unregisters NML3ConfigData instances
+that should be configured. Thereby they also say whether ACD should be done for the IPv4 addresses.
+NML3Cfg then keeps state for each IPv4 address, whether ACD should be performed, and whether the
+address is ready to be configured. NML3Cfg does not do DHCP or similar. That is still the responsibility
+of NMDevice to run a NMDhcpClient. But it does run ACD, because whether to perform ACD on an address
+requires a holistic view of all addresses of that interface. For example, if you configure a static
+IP address 192.168.2.5 (with ACD disabled) and you also get the same address via DHCP, then ACD should
+not performed for that address (even if the user configured ACD with DHCP). Of course, that is a very
+unlikely example. More likely is that NetworkManager is restarted and it leaves the addresses (that passed
+ACD) configured. After restart, DHCP finds the same addresses and no new ACD should be performed. This shows
+that the ACD state depends all the IP addresses on an interface,
+and thus it's done by NML3Cfg. The API for this is very simple. Users enable/disable ACD during nm_l3cfg_add_config()
+and receive events like [NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L303).
+Another advantage is that ACD now works for any kinds of addresses. Currently it only works for addresses
+from DHCP and link local addresses.
+
+NML3Cfg does not implement or drive DHCP. However, as it already does ACD it gained it's own IPv4LL
+"library": [NML3IPv4LL](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L3624).
+This will replace nettools' n-ipv4ll library, because that library also does ACD internally, while we want
+to use the holistic view that NML3Cfg has. What this means, is that the user (NMDevice)
+can request a NML3IPv4LL handle from the NML3Cfg instance, and it just does it with a simple API.
+All the user might do is to enable/disable the handle and to react to signals (if it cares to find
+out whether IPv4LL fails).
+
+The general parts of NML3Cfg are already implemented. It has unit tests and can be tested independently.
+You might note that NML3Cfg is not trivial already, but the API that it provides is as simple as possible:
+create immutable NML3ConfigData instance, and add/remove them. Optionally, handle the ACD events and
+listen to some events. The complexity that NML3Cfg has, will lead in the same amount simplify NMDevice.
+
+What is missing is NMDevice using this new API. Instead of creating and tracking NMIP4Config instances,
+it needs to track NML3ConfigData instances. In principle that sounds simple, in practice that changes
+large part of "nm-device.c".
+
+Thereby also the state machine for NM_DEVICE_STATE will be improved. It's anyway a rewrite. This will lay the
+groundwork for more flexible configuration of IP methods, with different failure modes (opportunistic or
+mandatory).
+
+What then also should be easier, to combine IPv4LL with other addressing methods. In Windows AFAIK, if you
+don't get a DHCP address it will configure a IPv4LL address. That is also what RFC suggests, but which we
+currently don't support.
+
+In general, change the way how external IP addresses/routes are tracked. This merge, intersect, subtract
+approach does not perform well. Currently we react on signals and it's hard to understand what happens
+in response to that, or whether it's really the correct thing to do. See yourself starting from
+[here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/devices/nm-device.c#L14214).
+
+### DHCP
+
+Currently, when NMDhcpClient receives a lease, it emits a signal with two things: the NMIP4Config
+instance (containing addresses, routes, DNS settings and other information for later use), and a string
+dictionary with the DHCP lease options (they are mainly used to expose them on D-Bus). The latter is
+immutable (meaning, it's not changed afterwards). That does not significantly change with L3Cfg. The
+difference is that instead of NMIP4Config a NML3ConfigData instance gets created. That instance then
+references the (immutable) strdict. With that, any part of the code that has access to the NML3ConfigData,
+also has access to the lease options. So instead of two separate
+pieces of information, the result of a lease event will only be a NML3ConfigData instance (which internally
+tracks the strdict with the DHCP lease options).
+
+Later, when NML3Cfg configures an interface, it takes all NML3ConfigData instances that were added to
+it, and merges them. [Currently](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3-config-data.c#L2693),
+the merged data will not contain the lease information, but it's probably not needed anyway.
+
+If it would be needed, the question is what happens if multiple lease informations are present
+during the merge. Duplicate leases would not commonly happen, but in general, the merging algorithm
+needs to take into account priorities and conflicting data.
+That is done by users who call [add](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2658)
+to provide a priority for the NML3ConfigData instance.
+Later, the instances get sorted by priority and merging is smart to take that into account
+([here](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/nm-l3cfg.c#L2983)).
+
+Also, we currently inject the route-metric and table into the generated NMIP4Config.
+Those settings come from the connection profiles and not from DHCP. We will avoid that
+by allowing the routes in NML3ConfigData to be marked as metric\_any and table\_any.
+That way,the NML3ConfigData is independent (and immutable) with respect to those settings.
+The same happens for example with PPP, where the modem starts PPP, and currently the
+route and metric needs to be passed several layers down. But worst, those settings
+can change during reapply. Currently that means we need to hack NMIP4Config with
+those changes. Later, we will only tell NML3Cfg to track the NML3ConfigData with
+different settings.
+
+### DNS
+
+DNS information is currently set in the NMIP4Config instances. That happens for example with the DNS information
+from a DHCP lease, but also with the static DNS settings from the connection profile. Later, the same information
+will packed in NML3ConfigData.
+
+One nice difference is again the immutability. Currently, NMDnsManager keeps a reference to all relevant NMIP4Config instances,
+but as they are mutable, it needs to [subscribe](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/6b64fac06d2f6e0d9fa530ebb1ab28d53a1c5d03/src/core/dns/nm-dns-manager.c#L275)
+to changes. Later, when a NML3ConfigData instance "changes", it means it was
+replaced by a different one and NMDnsManager needs to update its list of tracked NML3ConfigData. I find that
+cleaner, because adding and removal to the list of NMIP4Config/NML3ConfigData happens anyway and needs to be handled.
+
+
+Related Bugs
+------------
+
+* Main bug:
+
+  - [rh#1868254](https://bugzilla.redhat.com/show_bug.cgi?id=1868254):
+    "refactor NetworkManager's IP configuration done by NMDevice"
+
+* Follow up but to improve model of devices:
+
+  - [rh#1066703](https://bugzilla.redhat.com/show_bug.cgi?id=1066703):
+    "\[RFE\] Handle parent/child relationships more cleanly"
+
+* Flexible IP methods:
+
+  - [rh#1791624](https://bugzilla.redhat.com/show_bug.cgi?id=1791624):
+    "NetworkManager must not remove used bridge"
+
+* Improving performance issues, this will lay ground work:
+
+  - [rh#1847125](https://bugzilla.redhat.com/show_bug.cgi?id=1847125):
+    "\[RFE\] Improve 20% performance on creating 1000 bridge over 1000 VLANs"
+
+  - [rh#1861527](https://bugzilla.redhat.com/show_bug.cgi?id=1861527):
+    "Excessive memory and CPU usage on a router with IPv6 BGP feed"
+
+  - [rh#1753677](https://bugzilla.redhat.com/show_bug.cgi?id=1753677):
+    "High cpu usage while non-controlled interface is mangling tc filters"
+
+TODO
+----
+
+- Before considering an IP method completely activated, check that all addresses
+  we want to configure are no longer tentative.
+  For example, when activating a `connection.type=pppoe`, we might get an IPv6 address
+  from the pppd daemon. We set that via
+  `nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, ip_data->l3cd)`
+  Note that this currently already is sufficient to make the IP method (as far as PPP
+  is concerned), to be "ready".
+  We should however wait for IPv6 DAD to complete. See how that is already done
+  for ipmanual (`_dev_ipmanual_check_ready()`).
+
+- NMDevicePPP fails to re-activate a profile that is currently active. This is not
+  new on "next" branch, but also happened before. Have a pppoe profile active,
+  and issue `nmcli connection up $PROFILE` again. This, as expected, brings down
+  the device but fails to re-activate it. It's not trivial to fix (I think), because
+  of the ip-ifindex madness. Check the logs. Test with contrib/scripts/test-ppp.sh
+  script. Possibly affects other types.
+
+- NMDevice's ip4_config_pre_commit() had only one effect, to call nm_modem_ip4_pre_commit()
+  which set IFF_NOARP. That is currently dropped. Find a different way to achieve that.
+  Theoretically, we still could do that from NML3Cfg's post-commit notification. But
+  this really should be handled by NML3Cfg. That means, to disable IFF_NOARP needs somehow
+  configured by NMDeviceModem -- possibly by setting a flag in NML3ConfigData that indicates
+  to do this. Or maybe NML3Cfg should detect automatically when to set IFF_NOARP.
+
+- `ipvx.method=auto` usually means autoconf6/DHCPv4, but it doesn't have to. For example
+  with PPP/VPN it might mean that the IP configuration is provided by pppd/VPN or with
+  Wi-Fi-P2P it might mean that the controller of a peer runs a DHCP server (shared).
+  Now the parents implementation activate_stage3_ip_config() can no longer be overwritten
+  by subclasses, and they always perform their steps. For subclasses to modify what a
+  method means there is klass->get_ip_method_auto(self(). NMDeviceModem does not yet
+  implement that, which is most certainly lacking. We need to test at least NMDeviceModem
+  whether IP methods work correctly in this regard. Possibly other subclasses are affected
+  too (NMDeviceBluetooth?).
diff --git a/src/core/README.md b/src/core/README.md
new file mode 100644
index 00000000..13779197
--- /dev/null
+++ b/src/core/README.md
@@ -0,0 +1,9 @@
+core
+====
+
+The source code of the NetworkManager daemon.
+
+NetworkManager is a daemon that provides a D-Bus API and a file-based
+API for configuring the network on a Linux host.
+
+This is the daemon source code.
diff --git a/src/core/README.next.ip-config.md b/src/core/README.next.ip-config.md
new file mode 100644
index 00000000..e5be74f1
--- /dev/null
+++ b/src/core/README.next.ip-config.md
@@ -0,0 +1,59 @@
+Rework `NMIP[46]Config` for `next` branch
+=========================================
+
+The `next` branch is a large rework of internals, how IP configuration is done by `NMDevice`.
+
+Previously, there are two `GObject`s named `NMIP4Config` and `NMIP6Config`. These
+serve different purposes:
+
+1) They are data containers that can track IP configuration. As such, `NMDevice`
+   and various parts (like `NMDhcpClient`) create them, pass them around and
+   mutate/merge them to track the IP configuration.
+
+2) They are also subclasses of `NMDBusObject` and exported on D-Bus as
+   `/org/freedesktop/NetworkManager/IP4Config/1`, etc. As such, see their
+   [D-Bus API](../../introspection/org.freedesktop.NetworkManager.IP4Config.xml)
+   (and [for IPv6](../../introspection/org.freedesktop.NetworkManager.IP6Config.xml)).
+
+`next` branch will replace use 1) with `NML3ConfigData`. `NML3ConfigData` are immutable
+(sealable) data containers with little logic. This leaves `NMIP4Config` to only
+implement 2).
+
+This needs to be reworked.
+
+* Now `NMIP4Config` and `NMIP6Config` are subclasses of `NMIPConfig`. The goal
+  is to treat IPv4/IPv6 similar and generically. Probably there should be very
+  little code in the subclasses left and most should move to the parent classes.
+  We still need separate GObject types though, because that is how `NMDBusObject`'s
+  glue code can handle different D-Bus paths.
+
+* Now `NML3Cfg` is a handle for the IP configuration parameters of a device (ifindex).
+  As `NMIPConfig` mostly is about exporting the current IP configuration, it probably
+  can get most of it from there (and by listening to signals to that).
+
+* Note that `NMDevice`, `NMActiveConnection` refer `NMIP[46]Config`s, and most
+  importantly, the respective D-Bus objects refer to them. As `NMVpnConnection`
+  (and "org.freedesktop.NetworkManager.VPN.Connection" interface) are modeled
+  as "subclasses" of `NMActiveConnection`, they also have one. That means,
+  it's not entirely clear what these properties even are. For example, currently,
+  `NMDevice` does a (terrible) dance of tracking external `NMIP[46]Config` objects,
+  merging, intersecting and subtracting them with other `NMIP4Config` objects
+  to get the merged one, which is then exported on D-Bus. That merged object
+  does therefore not directly expose the IP addresses that are actually
+  configured on the interface (`ip addr`), but more what NetworkManager
+  wanted to configure and the (terrible) feedback loop where the platform
+  addresses get synced. With `next` branch and `NML3Cfg` there is a clear distinction
+  between what NetworkManager wants to configure vs. what is actually configured.
+  I think for `NMDevice` and `NMActiveConnection`, the IP addresses on
+  "org.freedesktop.NetworkManager.IP4Config" should expose the IP addresses
+  that are actually in platform (`ip addr`). If there is a need to expose
+  additional information (like things that NetworkManager wanted to configure),
+  then this should be different/new API.
+  On the other hand, currently `NMVpnConnection`'s `NMIP4Config` only tracks the 
+  IP addresses that come from the VPN plugin. So it's much more what it wants
+  to configure (from the VPN plugin), and not at all about what is configured
+  on the interface.
+  I think that needs to change. A `NMIPConfig` object on D-Bus exposes IP configuration
+  information about an netdev interface. Period. That also means that a `NMVpnConnection`
+  (which currently is like a active connection associated with the device) links to
+  the same `NMIPConfig` object as the underlying device.
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index a3467930..98670c8d 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -922,7 +922,7 @@ deactivate(NMDevice *device)
 static void
 nm_device_bond_init(NMDeviceBond *self)
 {
-    nm_assert(nm_device_is_master(NM_DEVICE(self)));
+    nm_assert(nm_device_is_controller(NM_DEVICE(self)));
 }
 
 static const NMDBusInterfaceInfoExtended interface_info_device_bond = {
@@ -958,7 +958,7 @@ nm_device_bond_class_init(NMDeviceBondClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_BOND_SETTING_NAME;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_BOND);
 
-    device_class->is_master                = TRUE;
+    device_class->is_controller            = TRUE;
     device_class->get_generic_capabilities = get_generic_capabilities;
     device_class->complete_connection      = complete_connection;
 
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index ab3a6be9..2405beea 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -1159,7 +1159,7 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne
 static void
 nm_device_bridge_init(NMDeviceBridge *self)
 {
-    nm_assert(nm_device_is_master(NM_DEVICE(self)));
+    nm_assert(nm_device_is_controller(NM_DEVICE(self)));
 }
 
 static const NMDBusInterfaceInfoExtended interface_info_device_bridge = {
@@ -1194,7 +1194,7 @@ nm_device_bridge_class_init(NMDeviceBridgeClass *klass)
     device_class->connection_type_supported = NM_SETTING_BRIDGE_SETTING_NAME;
     device_class->link_types                = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_BRIDGE);
 
-    device_class->is_master                   = TRUE;
+    device_class->is_controller               = TRUE;
     device_class->mtu_force_set               = TRUE;
     device_class->get_generic_capabilities    = get_generic_capabilities;
     device_class->check_connection_compatible = check_connection_compatible;
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 0f7f9d65..8c2a9643 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -370,7 +370,7 @@ check_connection_compatible(NMDevice     *device,
     if (s_wired) {
         const char        *mac, *perm_hw_addr;
         gboolean           try_mac = TRUE;
-        const char *const *mac_blacklist;
+        const char *const *mac_denylist;
         int                i;
 
         if (!match_subchans(self, s_wired, &try_mac)) {
@@ -390,17 +390,17 @@ check_connection_compatible(NMDevice     *device,
                 return FALSE;
             }
 
-            /* Check for MAC address blacklist */
-            mac_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired);
-            for (i = 0; mac_blacklist[i]; i++) {
-                if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) {
+            /* Check for MAC address denylist */
+            mac_denylist = nm_setting_wired_get_mac_address_denylist(s_wired);
+            for (i = 0; mac_denylist[i]; i++) {
+                if (!nm_utils_hwaddr_valid(mac_denylist[i], ETH_ALEN)) {
                     nm_utils_error_set_literal(error,
                                                NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                                "invalid MAC in blacklist");
                     return FALSE;
                 }
 
-                if (nm_utils_hwaddr_matches(mac_blacklist[i], -1, perm_hw_addr, -1)) {
+                if (nm_utils_hwaddr_matches(mac_denylist[i], -1, perm_hw_addr, -1)) {
                     nm_utils_error_set_literal(error,
                                                NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
                                                "permanent MAC address of device blacklisted");
diff --git a/src/core/devices/nm-device-factory.c b/src/core/devices/nm-device-factory.c
index 69c2a38f..22c8fa5a 100644
--- a/src/core/devices/nm-device-factory.c
+++ b/src/core/devices/nm-device-factory.c
@@ -183,7 +183,8 @@ nm_device_factory_class_init(NMDeviceFactoryClass *klass)
 static GHashTable *factories_by_link    = NULL;
 static GHashTable *factories_by_setting = NULL;
 
-static void __attribute__((destructor)) _cleanup(void)
+static void __attribute__((destructor))
+_cleanup(void)
 {
     nm_clear_pointer(&factories_by_link, g_hash_table_unref);
     nm_clear_pointer(&factories_by_setting, g_hash_table_unref);
diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c
index ed0a2738..80909805 100644
--- a/src/core/devices/nm-device-utils.c
+++ b/src/core/devices/nm-device-utils.c
@@ -128,8 +128,20 @@ NM_UTILS_LOOKUP_STR_DEFINE(
     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_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED,
-                             "device-handler-failed"), );
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED, "device-handler-failed"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT, "unmanaged-by-default"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN,
+                             "unmanaged-external-down"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT,
+                             "unmanaged-link-not-init"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING, "unmanaged-quitting"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING, "unmanaged-sleeping"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF, "unmanaged-user-conf"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT,
+                             "unmanaged-user-explicit"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS,
+                             "unmanaged-user-settings"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"), );
 
 NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string,
                            NMDeviceMtuSource,
diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c
index a13de1cb..6c7adc6c 100644
--- a/src/core/devices/nm-device-vrf.c
+++ b/src/core/devices/nm-device-vrf.c
@@ -329,7 +329,7 @@ nm_device_vrf_class_init(NMDeviceVrfClass *klass)
 
     device_class->connection_type_supported        = NM_SETTING_VRF_SETTING_NAME;
     device_class->connection_type_check_compatible = NM_SETTING_VRF_SETTING_NAME;
-    device_class->is_master                        = TRUE;
+    device_class->is_controller                    = TRUE;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_VRF);
 
     device_class->attach_port                 = attach_port;
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 34022efb..5b377529 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -95,6 +95,9 @@
 #define CARRIER_WAIT_TIME_MS             6000
 #define CARRIER_WAIT_TIME_AFTER_MTU_MSEC 10000
 
+#define SECONDS_PER_WEEK 604800
+#define SECONDS_PER_DAY  86400
+
 #define NM_DEVICE_AUTH_RETRIES_UNSET    -1
 #define NM_DEVICE_AUTH_RETRIES_INFINITY -2
 #define NM_DEVICE_AUTH_RETRIES_DEFAULT  3
@@ -371,7 +374,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice,
                              PROP_IFINDEX,
                              PROP_AVAILABLE_CONNECTIONS,
                              PROP_PHYSICAL_PORT_ID,
-                             PROP_MASTER,
                              PROP_PARENT,
                              PROP_HW_ADDRESS,
                              PROP_PERM_HW_ADDRESS,
@@ -386,7 +388,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice,
                              PROP_IP4_CONNECTIVITY,
                              PROP_IP6_CONNECTIVITY,
                              PROP_INTERFACE_FLAGS,
-                             PROP_PORTS, );
+                             PROP_PORTS,
+                             PROP_CONTROLLER, );
 
 typedef struct _NMDevicePrivate {
     guint device_link_changed_id;
@@ -707,10 +710,10 @@ typedef struct _NMDevicePrivate {
         NMPlatformLinkChangeFlags flags;
     } link_props_state;
 
-    /* master interface for bridge/bond/team slave */
-    NMDevice *master;
-    gulong    master_ready_id;
-    int       master_ifindex;
+    /* controller interface for bridge/bond/team slave */
+    NMDevice *controller;
+    gulong    controller_ready_id;
+    int       controller_ifindex;
 
     /* slave management */
     CList slaves; /* list of SlaveInfo */
@@ -1523,11 +1526,12 @@ _prop_get_ipvx_route_table(NMDevice *self, int addr_family)
     if (route_table == 0u && connection
         && (s_con = nm_connection_get_setting_connection(connection))
         && (nm_streq0(nm_setting_connection_get_port_type(s_con), NM_SETTING_VRF_SETTING_NAME)
-            && priv->master && nm_device_get_device_type(priv->master) == NM_DEVICE_TYPE_VRF)) {
+            && priv->controller
+            && nm_device_get_device_type(priv->controller) == NM_DEVICE_TYPE_VRF)) {
         const NMPlatformLnkVrf *lnk;
 
         lnk = nm_platform_link_get_lnk_vrf(nm_device_get_platform(self),
-                                           nm_device_get_ifindex(priv->master),
+                                           nm_device_get_ifindex(priv->controller),
                                            NULL);
 
         if (lnk)
@@ -2269,6 +2273,7 @@ _prop_get_ipv4_dhcp_vendor_class_identifier(NMDevice *self, NMSettingIP4Config *
 static NMSettingIP6ConfigPrivacy
 _prop_get_ipv6_ip6_privacy(NMDevice *self)
 {
+    NMDevicePrivate          *priv = NM_DEVICE_GET_PRIVATE(self);
     NMSettingIP6ConfigPrivacy ip6_privacy;
     NMConnection             *connection;
 
@@ -2302,16 +2307,100 @@ _prop_get_ipv6_ip6_privacy(NMDevice *self)
     if (!nm_device_get_ip_ifindex(self))
         return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
 
-    /* 3.) No valid default-value configured. Fallback to reading sysctl.
-     *
-     * Instead of reading static config files in /etc, just read the current sysctl value.
-     * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves
-     * the "default" entry untouched. */
-    ip6_privacy = nm_platform_sysctl_get_int32(
-        nm_device_get_platform(self),
-        NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv6/conf/default/use_tempaddr"),
-        NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
-    return _ip6_privacy_clamp(ip6_privacy);
+    /* 3.) No valid default value configured. Fall back to the original value
+     * from before NM started. */
+    return _ip6_privacy_clamp(_nm_utils_ascii_str_to_int64(
+        g_hash_table_lookup(priv->ip6_saved_properties, "use_tempaddr"),
+        10,
+        G_MININT32,
+        G_MAXINT32,
+        NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN));
+}
+
+static gint32
+_prop_get_ipv6_temp_valid_lifetime(NMDevice *self)
+{
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+    gint32           temp_valid_lifetime;
+    NMConnection    *connection;
+
+    g_return_val_if_fail(self, 0);
+
+    /* 1.) First look at the per-connection setting. If it is not 0 (unknown), use it. */
+    connection = nm_device_get_applied_connection(self);
+    if (connection) {
+        NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection);
+
+        if (s_ip6) {
+            temp_valid_lifetime =
+                nm_setting_ip6_config_get_temp_valid_lifetime(NM_SETTING_IP6_CONFIG(s_ip6));
+            if (temp_valid_lifetime)
+                return temp_valid_lifetime;
+        }
+    }
+
+    /* 2.) Use the default value from the configuration. */
+    temp_valid_lifetime =
+        nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                    NM_CON_DEFAULT("ipv6.temp-valid-lifetime"),
+                                                    self,
+                                                    0,
+                                                    G_MAXINT32,
+                                                    0);
+    if (temp_valid_lifetime)
+        return temp_valid_lifetime;
+
+    /* 3.) No valid default value configured. Fall back to the original value
+     * from before NM started. */
+    return _nm_utils_ascii_str_to_int64(
+        g_hash_table_lookup(priv->ip6_saved_properties, "temp_valid_lft"),
+        10,
+        0,
+        G_MAXINT32,
+        SECONDS_PER_WEEK /* final hardcoded fallback: 1 week */);
+}
+
+static gint32
+_prop_get_ipv6_temp_preferred_lifetime(NMDevice *self)
+{
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+    gint32           temp_preferred_lifetime;
+    NMConnection    *connection;
+
+    g_return_val_if_fail(self, 0);
+
+    /* 1.) First look at the per-connection setting. If it is not 0 (unknown), use it. */
+    connection = nm_device_get_applied_connection(self);
+    if (connection) {
+        NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection);
+
+        if (s_ip6) {
+            temp_preferred_lifetime =
+                nm_setting_ip6_config_get_temp_preferred_lifetime(NM_SETTING_IP6_CONFIG(s_ip6));
+            if (temp_preferred_lifetime)
+                return temp_preferred_lifetime;
+        }
+    }
+
+    /* 2.) Use the default value from the configuration. */
+    temp_preferred_lifetime =
+        nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                    NM_CON_DEFAULT("ipv6.temp-preferred-lifetime"),
+                                                    self,
+                                                    0,
+                                                    G_MAXINT32,
+                                                    0);
+    if (temp_preferred_lifetime)
+        return temp_preferred_lifetime;
+
+    /* 3.) No valid default value configured. Fall back to the original value
+     * from before NM started. */
+    return _nm_utils_ascii_str_to_int64(
+        g_hash_table_lookup(priv->ip6_saved_properties, "temp_prefered_lft"),
+        10,
+        0,
+        G_MAXINT32,
+        SECONDS_PER_DAY /* final hardcoded fallback: 1 day */);
 }
 
 static NMSettingIP6ConfigAddrGenMode
@@ -2359,11 +2448,16 @@ _prop_get_ipv6_addr_gen_mode(NMDevice *self)
 }
 
 static const char *
-_prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolean is_wifi)
+_prop_get_x_cloned_mac_address(NMDevice     *self,
+                               NMConnection *connection,
+                               gboolean      is_wifi,
+                               gboolean     *out_is_default_special)
 {
     NMSetting  *setting;
     const char *addr = NULL;
 
+    NM_SET_OUT(out_is_default_special, FALSE);
+
     setting = nm_connection_get_setting(connection,
                                         is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED);
     if (setting) {
@@ -2394,11 +2488,17 @@ _prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolea
                     NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
                     NM_SETTING_MAC_RANDOMIZATION_ALWAYS,
                     NM_SETTING_MAC_RANDOMIZATION_DEFAULT);
-                if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS)
+                if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) {
                     addr = NM_CLONED_MAC_RANDOM;
+                    NM_SET_OUT(out_is_default_special, TRUE);
+                }
             }
-        } else if (NM_CLONED_MAC_IS_SPECIAL(a, is_wifi) || nm_utils_hwaddr_valid(a, ETH_ALEN))
+        } else if (NM_CLONED_MAC_IS_SPECIAL(a, is_wifi)) {
+            addr = a;
+            NM_SET_OUT(out_is_default_special, TRUE);
+        } else if (nm_utils_hwaddr_valid(a, ETH_ALEN)) {
             addr = a;
+        }
     }
 
     return addr;
@@ -3726,7 +3826,7 @@ _dev_ip_state_check(NMDevice *self, int addr_family)
     }
 
     if (priv->ip_data_x[IS_IPv4].state == NM_DEVICE_IP_STATE_PENDING
-        && nm_active_connection_get_master(NM_ACTIVE_CONNECTION(priv->act_request.obj))
+        && nm_active_connection_get_controller(NM_ACTIVE_CONNECTION(priv->act_request.obj))
         && !priv->is_enslaved) {
         /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the
          * master to enslave us. */
@@ -6676,14 +6776,14 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
 static void
 detach_port_cb(NMDevice *self, GError *error, gpointer user_data)
 {
-    nm_auto_unref_object NMDevice *slave      = user_data;
-    NMDevicePrivate               *slave_priv = NM_DEVICE_GET_PRIVATE(slave);
+    nm_auto_unref_object NMDevice *slave     = user_data;
+    NMDevicePrivate               *port_priv = NM_DEVICE_GET_PRIVATE(slave);
 
-    nm_assert(slave_priv->port_detach_count > 0);
+    nm_assert(port_priv->port_detach_count > 0);
 
-    if (--slave_priv->port_detach_count == 0) {
-        if (slave_priv->state == NM_DEVICE_STATE_DEACTIVATING) {
-            deactivate_ready(slave, slave_priv->port_detach_reason);
+    if (--port_priv->port_detach_count == 0) {
+        if (port_priv->state == NM_DEVICE_STATE_DEACTIVATING) {
+            deactivate_ready(slave, port_priv->port_detach_reason);
         }
     }
 }
@@ -6708,7 +6808,7 @@ nm_device_master_release_slave(NMDevice           *self,
                                NMDeviceStateReason reason)
 {
     NMDevicePrivate          *priv;
-    NMDevicePrivate          *slave_priv;
+    NMDevicePrivate          *port_priv;
     SlaveInfo                *info;
     gs_unref_object NMDevice *self_free  = NULL;
     gs_unref_object NMDevice *slave_free = NULL;
@@ -6735,10 +6835,10 @@ nm_device_master_release_slave(NMDevice           *self,
     if (!info)
         g_return_if_reached();
 
-    priv       = NM_DEVICE_GET_PRIVATE(self);
-    slave_priv = NM_DEVICE_GET_PRIVATE(slave);
+    priv      = NM_DEVICE_GET_PRIVATE(self);
+    port_priv = NM_DEVICE_GET_PRIVATE(slave);
 
-    g_return_if_fail(self == slave_priv->master);
+    g_return_if_fail(self == port_priv->controller);
     nm_assert(slave == info->slave);
     nm_clear_g_cancellable(&info->cancellable);
 
@@ -6754,8 +6854,8 @@ nm_device_master_release_slave(NMDevice           *self,
                                                      detach_port_cb,
                                                      g_object_ref(slave));
         if (ret == NM_TERNARY_DEFAULT) {
-            slave_priv->port_detach_count++;
-            slave_priv->port_detach_reason = reason;
+            port_priv->port_detach_count++;
+            port_priv->port_detach_reason = reason;
         }
     }
 
@@ -6763,9 +6863,9 @@ nm_device_master_release_slave(NMDevice           *self,
     nm_device_slave_notify_release(slave, reason, release_type);
 
     /* keep both alive until the end of the function.
-     * Transfers ownership from slave_priv->master.  */
-    nm_assert(self == slave_priv->master);
-    self_free = g_steal_pointer(&slave_priv->master);
+     * Transfers ownership from port_priv->controller.  */
+    nm_assert(self == port_priv->controller);
+    self_free = g_steal_pointer(&port_priv->controller);
 
     nm_assert(slave == info->slave);
     slave_free = g_steal_pointer(&info->slave);
@@ -6900,7 +7000,7 @@ carrier_changed(NMDevice *self, gboolean carrier)
     if (priv->ignore_carrier && !carrier)
         return;
 
-    if (nm_device_is_master(self)) {
+    if (nm_device_is_controller(self)) {
         if (carrier) {
             /* If needed, also resume IP configuration that is
              * waiting for carrier. */
@@ -7069,7 +7169,7 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
         plink_master            = nm_platform_link_get(nm_device_get_platform(self), plink->master);
         plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master));
     } else {
-        if (priv->master_ifindex == 0)
+        if (priv->controller_ifindex == 0)
             goto out;
         master       = NULL;
         plink_master = NULL;
@@ -7082,17 +7182,17 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
         goto out;
     }
 
-    priv->master_ifindex = plink->master;
+    priv->controller_ifindex = plink->master;
 
-    if (priv->master) {
-        if (plink->master > 0 && plink->master == nm_device_get_ifindex(priv->master)) {
+    if (priv->controller) {
+        if (plink->master > 0 && plink->master == nm_device_get_ifindex(priv->controller)) {
             /* call add-slave again. We expect @self already to be added to
              * the master, but this also triggers a recheck-assume. */
-            nm_device_master_add_slave(priv->master, self, FALSE);
+            nm_device_master_add_slave(priv->controller, self, FALSE);
             goto out;
         }
 
-        nm_device_master_release_slave(priv->master,
+        nm_device_master_release_slave(priv->controller,
                                        self,
                                        RELEASE_SLAVE_TYPE_NO_CONFIG,
                                        NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
@@ -7133,9 +7233,9 @@ device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
-    g_return_if_fail(priv->master_ifindex > 0);
+    g_return_if_fail(priv->controller_ifindex > 0);
 
-    if (priv->master_ifindex != nm_device_get_ifindex(device_changed))
+    if (priv->controller_ifindex != nm_device_get_ifindex(device_changed))
         return;
 
     _LOGD(LOGD_DEVICE,
@@ -8157,7 +8257,7 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error)
     if (nm_clear_g_free(&priv->ip_iface_))
         update_prop_ip_iface(self);
 
-    priv->master_ifindex = 0;
+    priv->controller_ifindex = 0;
 
     _set_mtu(self, 0);
 
@@ -8394,7 +8494,7 @@ static gboolean
 nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
 {
     NMDevicePrivate *priv;
-    NMDevicePrivate *slave_priv;
+    NMDevicePrivate *port_priv;
     SlaveInfo       *info;
     gboolean         changed = FALSE;
 
@@ -8402,8 +8502,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
     g_return_val_if_fail(NM_IS_DEVICE(slave), FALSE);
     g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port, FALSE);
 
-    priv       = NM_DEVICE_GET_PRIVATE(self);
-    slave_priv = NM_DEVICE_GET_PRIVATE(slave);
+    priv      = NM_DEVICE_GET_PRIVATE(self);
+    port_priv = NM_DEVICE_GET_PRIVATE(slave);
 
     info = find_slave_info(self, slave);
 
@@ -8417,8 +8517,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
         g_return_val_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
 
     if (!info) {
-        g_return_val_if_fail(!slave_priv->master, FALSE);
-        g_return_val_if_fail(!slave_priv->is_enslaved, FALSE);
+        g_return_val_if_fail(!port_priv->controller, FALSE);
+        g_return_val_if_fail(!port_priv->is_enslaved, FALSE);
 
         info            = g_slice_new0(SlaveInfo);
         info->slave     = g_object_ref(slave);
@@ -8426,25 +8526,25 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
         info->watch_id =
             g_signal_connect(slave, NM_DEVICE_STATE_CHANGED, G_CALLBACK(slave_state_changed), self);
         c_list_link_tail(&priv->slaves, &info->lst_slave);
-        slave_priv->master = g_object_ref(self);
+        port_priv->controller = g_object_ref(self);
 
         _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES);
 
         /* no need to emit
          *
-         *   _notify (slave, PROP_MASTER);
+         *   _notify (slave, PROP_CONTROLLER);
          *
-         * because slave_priv->is_enslaved is not true, thus the value
+         * because port_priv->is_enslaved is not true, thus the value
          * didn't change yet. */
 
-        g_warn_if_fail(!NM_FLAGS_HAS(slave_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE));
+        g_warn_if_fail(!NM_FLAGS_HAS(port_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE));
         nm_device_set_unmanaged_by_flags(slave,
                                          NM_UNMANAGED_IS_SLAVE,
                                          NM_UNMAN_FLAG_OP_SET_MANAGED,
                                          NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
         changed = TRUE;
     } else
-        g_return_val_if_fail(slave_priv->master == self, FALSE);
+        g_return_val_if_fail(port_priv->controller == self, FALSE);
 
     nm_device_queue_recheck_assume(self);
     nm_device_queue_recheck_assume(slave);
@@ -8525,38 +8625,40 @@ nm_device_master_release_slaves_all(NMDevice *self)
 }
 
 /**
- * nm_device_is_master:
+ * nm_device_is_controller:
  * @self: the device
  *
- * Returns: %TRUE if the device can have slaves
+ * Returns: %TRUE if the device can have ports
  */
 gboolean
-nm_device_is_master(NMDevice *self)
+nm_device_is_controller(NMDevice *self)
 {
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
 
-    return NM_DEVICE_GET_CLASS(self)->is_master;
+    return NM_DEVICE_GET_CLASS(self)->is_controller;
 }
 
 /**
- * nm_device_get_master:
+ * nm_device_get_controller:
  * @self: the device
  *
- * If @self has been enslaved by another device, this returns that
+ * If @self has been set as port by another device, this returns that
  * device. Otherwise, it returns %NULL. (In particular, note that if
- * @self is in the process of activating as a slave, but has not yet
- * been enslaved by its master, this will return %NULL.)
+ * @self is in the process of activating as a port, but has not yet
+ * been set as port by its controller, this will return %NULL.)
+ *
+ * Returns: (transfer none): @self's controller, or %NULL
  *
- * Returns: (transfer none): @self's master, or %NULL
+ * Since: 1.48
  */
 NMDevice *
-nm_device_get_master(NMDevice *self)
+nm_device_get_controller(NMDevice *self)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
     if (priv->is_enslaved) {
-        g_return_val_if_fail(priv->master, NULL);
-        return priv->master;
+        g_return_val_if_fail(priv->controller, NULL);
+        return priv->controller;
     }
     return NULL;
 }
@@ -8576,7 +8678,7 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success)
     NMConnection    *connection = nm_device_get_applied_connection(self);
     gboolean         activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG);
 
-    g_return_if_fail(priv->master);
+    g_return_if_fail(priv->controller);
 
     if (!priv->is_enslaved) {
         if (success) {
@@ -8585,14 +8687,15 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success)
                       "Activation: connection '%s' enslaved, continuing activation",
                       nm_connection_get_id(connection));
             } else
-                _LOGI(LOGD_DEVICE, "enslaved to %s", nm_device_get_iface(priv->master));
+                _LOGI(LOGD_DEVICE, "enslaved to %s", nm_device_get_iface(priv->controller));
 
             priv->is_enslaved = TRUE;
 
-            _notify(self, PROP_MASTER);
+            _notify(priv->controller, PROP_CONTROLLER);
 
-            nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref);
-            nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES);
+            nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->controller)->ports_variant,
+                             g_variant_unref);
+            nm_gobject_notify_together(priv->controller, PROP_PORTS, PROP_SLAVES);
         } else if (activating) {
             _LOGW(LOGD_DEVICE,
                   "Activation: connection '%s' could not be enslaved",
@@ -8629,7 +8732,7 @@ nm_device_slave_notify_release(NMDevice           *self,
     NMConnection    *connection = nm_device_get_applied_connection(self);
     const char      *master_status;
 
-    g_return_if_fail(priv->master);
+    g_return_if_fail(priv->controller);
 
     if (!priv->is_enslaved && release_type == RELEASE_SLAVE_TYPE_NO_CONFIG)
         return;
@@ -8661,14 +8764,14 @@ nm_device_slave_notify_release(NMDevice           *self,
         _cancel_activation(self);
         nm_device_queue_state(self, NM_DEVICE_STATE_DEACTIVATING, reason);
     } else
-        _LOGI(LOGD_DEVICE, "released from master device %s", nm_device_get_iface(priv->master));
+        _LOGI(LOGD_DEVICE, "released from master device %s", nm_device_get_iface(priv->controller));
 
     priv->is_enslaved = FALSE;
 
-    _notify(self, PROP_MASTER);
+    _notify(priv->controller, PROP_CONTROLLER);
 
-    nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref);
-    nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES);
+    nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->controller)->ports_variant, g_variant_unref);
+    nm_gobject_notify_together(priv->controller, PROP_PORTS, PROP_SLAVES);
 }
 
 /**
@@ -8692,10 +8795,10 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config)
     _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE);
 
     priv = NM_DEVICE_GET_PRIVATE(self);
-    if (priv->master) {
+    if (priv->controller) {
         /* this is called when something externally messes with the slave or during shut-down.
          * Release the slave from master, but don't touch the device. */
-        nm_device_master_release_slave(priv->master,
+        nm_device_master_release_slave(priv->controller,
                                        self,
                                        RELEASE_SLAVE_TYPE_NO_CONFIG,
                                        NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
@@ -8728,7 +8831,7 @@ is_available(NMDevice *self, NMDeviceCheckDevAvailableFlags flags)
         return TRUE;
 
     /* master types are always available even without carrier. */
-    if (nm_device_is_master(self))
+    if (nm_device_is_controller(self))
         return TRUE;
 
     return FALSE;
@@ -8768,7 +8871,7 @@ gboolean
 nm_device_ignore_carrier_by_default(NMDevice *self)
 {
     /* master types ignore-carrier by default. */
-    return nm_device_is_master(self);
+    return nm_device_is_controller(self);
 }
 
 gboolean
@@ -9207,7 +9310,7 @@ nm_device_generate_connection(NMDevice *self,
         && NM_IN_STRSET(ip6_method,
                         NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
                         NM_SETTING_IP6_CONFIG_METHOD_DISABLED)
-        && !nm_setting_connection_get_master(NM_SETTING_CONNECTION(s_con))
+        && !nm_setting_connection_get_controller(NM_SETTING_CONNECTION(s_con))
         && c_list_is_empty(&priv->slaves)) {
         NM_SET_OUT(out_maybe_later, TRUE);
         g_set_error_literal(
@@ -9223,7 +9326,7 @@ nm_device_generate_connection(NMDevice *self,
      */
     if (nm_streq0(ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)
         && nm_streq0(ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)
-        && !nm_setting_connection_get_master(NM_SETTING_CONNECTION(s_con))
+        && !nm_setting_connection_get_controller(NM_SETTING_CONNECTION(s_con))
         && c_list_is_empty(&priv->slaves)
         && !nm_config_data_get_assume_ipv6ll_only(NM_CONFIG_GET_DATA, self)) {
         _LOGD(LOGD_DEVICE,
@@ -9492,7 +9595,7 @@ nm_device_check_slave_connection_compatible(NMDevice *self, NMConnection *slave)
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
     g_return_val_if_fail(NM_IS_CONNECTION(slave), FALSE);
 
-    if (!nm_device_is_master(self))
+    if (!nm_device_is_controller(self))
         return FALSE;
 
     /* All masters should have connection type set */
@@ -9508,6 +9611,17 @@ nm_device_check_slave_connection_compatible(NMDevice *self, NMConnection *slave)
     return nm_streq(connection_type, slave_type);
 }
 
+gboolean
+nm_device_can_be_parent(NMDevice *self)
+{
+    NMDeviceType device_type = nm_device_get_device_type(self);
+
+    if ((device_type == NM_DEVICE_TYPE_OVS_BRIDGE) || (device_type == NM_DEVICE_TYPE_OVS_PORT))
+        return FALSE;
+    else
+        return TRUE;
+}
+
 /**
  * nm_device_can_assume_connections:
  * @self: #NMDevice instance
@@ -9790,17 +9904,17 @@ master_ready(NMDevice *self, NMActiveConnection *active)
     NMActiveConnection *master_connection;
     NMDevice           *master;
 
-    /* Notify a master device that it has a new slave */
-    nm_assert(nm_active_connection_get_master_ready(active));
+    /* Notify a controller device that it has a new port */
+    nm_assert(nm_active_connection_get_controller_ready(active));
 
-    master_connection = nm_active_connection_get_master(active);
+    master_connection = nm_active_connection_get_controller(active);
 
     master = nm_active_connection_get_device(master_connection);
 
     _LOGD(LOGD_DEVICE, "master connection ready; master device %s", nm_device_get_iface(master));
 
-    if (priv->master && priv->master != master)
-        nm_device_master_release_slave(priv->master,
+    if (priv->controller && priv->controller != master)
+        nm_device_master_release_slave(priv->controller,
                                        self,
                                        RELEASE_SLAVE_TYPE_NO_CONFIG,
                                        NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
@@ -9816,7 +9930,7 @@ master_ready_cb(NMActiveConnection *active, GParamSpec *pspec, NMDevice *self)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
-    nm_assert(nm_active_connection_get_master_ready(active));
+    nm_assert(nm_active_connection_get_controller_ready(active));
 
     if (priv->state == NM_DEVICE_STATE_PREPARE)
         nm_device_activate_schedule_stage1_device_prepare(self, FALSE);
@@ -10063,7 +10177,7 @@ activate_stage1_device_prepare(NMDevice *self)
     }
 
     active = NM_ACTIVE_CONNECTION(priv->act_request.obj);
-    master = nm_active_connection_get_master(active);
+    master = nm_active_connection_get_controller(active);
     if (master) {
         if (nm_active_connection_get_state(master) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) {
             NMDevice           *master_device  = nm_active_connection_get_device(master);
@@ -10078,24 +10192,24 @@ activate_stage1_device_prepare(NMDevice *self)
             nm_device_state_changed(self, NM_DEVICE_STATE_FAILED, failure_reason);
             return;
         }
-        /* If the master connection is ready for slaves, attach ourselves */
-        if (!nm_active_connection_get_master_ready(active)) {
-            if (priv->master_ready_id == 0) {
-                _LOGD(LOGD_DEVICE, "waiting for master connection to become ready");
-                priv->master_ready_id =
+        /* If the controller connection is ready for ports, attach ourselves */
+        if (!nm_active_connection_get_controller_ready(active)) {
+            if (priv->controller_ready_id == 0) {
+                _LOGD(LOGD_DEVICE, "waiting for controller connection to become ready");
+                priv->controller_ready_id =
                     g_signal_connect(active,
-                                     "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
+                                     "notify::" NM_ACTIVE_CONNECTION_INT_CONTROLLER_READY,
                                      G_CALLBACK(master_ready_cb),
                                      self);
             }
             return;
         }
     }
-    nm_clear_g_signal_handler(priv->act_request.obj, &priv->master_ready_id);
+    nm_clear_g_signal_handler(priv->act_request.obj, &priv->controller_ready_id);
     if (master)
         master_ready(self, active);
-    else if (priv->master) {
-        nm_device_master_release_slave(priv->master,
+    else if (priv->controller) {
+        nm_device_master_release_slave(priv->controller,
                                        self,
                                        RELEASE_SLAVE_TYPE_CONFIG_FORCE,
                                        NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
@@ -10804,13 +10918,40 @@ _dev_ipdhcpx_set_state(NMDevice *self, int addr_family, NMDeviceIPState state)
 }
 
 static void
-_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean full_cleanup, gboolean release)
+_dev_ipdhcpx_cleanup(NMDevice *self, int addr_family, gboolean full_cleanup, gboolean force_release)
 {
-    NMDevicePrivate *priv    = NM_DEVICE_GET_PRIVATE(self);
-    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMDevicePrivate   *priv    = NM_DEVICE_GET_PRIVATE(self);
+    const int          IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMSettingIPConfig *s_ip4   = NULL;
+    NMSettingIPConfig *s_ip6   = NULL;
+    gboolean           release;
 
     _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_NONE);
 
+    if (IS_IPv4)
+        s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG);
+    else
+        s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG);
+
+    if ((IS_IPv4 && s_ip4) || (!IS_IPv4 && s_ip6)) {
+        if (nm_setting_ip_config_get_dhcp_send_release(IS_IPv4 ? s_ip4 : s_ip6)
+            == NM_TERNARY_DEFAULT)
+            release = nm_config_data_get_connection_default_int64(
+                NM_CONFIG_GET_DATA,
+                IS_IPv4 ? NM_CON_DEFAULT("ipv4.dhcp-send-release")
+                        : NM_CON_DEFAULT("ipv6.dhcp-send-release"),
+                self,
+                NM_TERNARY_FALSE,
+                NM_TERNARY_TRUE,
+                NM_TERNARY_FALSE);
+        else
+            release = nm_setting_ip_config_get_dhcp_send_release(IS_IPv4 ? s_ip4 : s_ip6);
+
+        release = force_release || (release && full_cleanup);
+    } else {
+        release = force_release;
+    }
+
     if (full_cleanup && !IS_IPv4) {
         priv->ipdhcp_data_6.v6.mode            = NM_NDISC_DHCP_LEVEL_NONE;
         priv->ipdhcp_data_6.v6.needed_prefixes = 0;
@@ -11306,7 +11447,7 @@ connection_requires_carrier(NMConnection *connection)
     /* We can progress to IP_CONFIG now, so that we're enslaved.
      * That may actually cause carrier to go up and thus continue activation. */
     s_con = nm_connection_get_setting_connection(connection);
-    if (nm_setting_connection_get_master(s_con))
+    if (nm_setting_connection_get_controller(s_con))
         return FALSE;
 
     ip4_carrier_wanted = connection_ip_method_requires_carrier(connection, AF_INET, &ip4_used);
@@ -11373,25 +11514,32 @@ nm_device_needs_ip6_subnet(NMDevice *self)
 void
 nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet)
 {
-    nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
-    char                                    sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
-    NMPlatformIP6Address                    address;
+    NMConnection *connection = nm_device_get_applied_connection(self);
 
-    l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED);
+    if (connection) {
+        NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection);
 
-    /* Assign a ::1 address in the subnet for us. */
-    address = *subnet;
-    address.address.s6_addr32[3] |= htonl(1);
+        if (nm_streq(nm_setting_ip_config_get_method(s_ip6), NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
+            nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
+            char                                    sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
+            NMPlatformIP6Address                    address;
+            l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED);
 
-    nm_l3_config_data_add_address_6(l3cd, &address);
+            /* Assign a ::1 address in the subnet for us. */
+            address = *subnet;
+            address.address.s6_addr32[3] |= htonl(1);
 
-    _LOGD(LOGD_IP6,
-          "ipv6-pd: using %s",
-          nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf)));
+            nm_l3_config_data_add_address_6(l3cd, &address);
 
-    _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, l3cd, FALSE);
-    _dev_l3_cfg_commit(self, TRUE);
-    _dev_ipac6_ndisc_set_router_config(self);
+            _LOGD(LOGD_IP6,
+                  "ipv6-pd: using %s",
+                  nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf)));
+
+            _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_PD_6, l3cd, FALSE);
+            _dev_l3_cfg_commit(self, TRUE);
+            _dev_ipac6_ndisc_set_router_config(self);
+        }
+    }
 }
 
 /*
@@ -11768,12 +11916,12 @@ _set_mtu(NMDevice *self, guint32 mtu)
     priv->mtu = mtu;
     _notify(self, PROP_MTU);
 
-    if (priv->master) {
+    if (priv->controller) {
         /* changing the MTU of a slave, might require the master to reset
          * its MTU. Note that the master usually cannot set a MTU larger
          * then the slave's. Hence, when the slave increases the MTU,
          * master might want to retry setting the MTU. */
-        nm_device_commit_mtu(priv->master);
+        nm_device_commit_mtu(priv->controller);
     }
 }
 
@@ -12377,6 +12525,8 @@ _dev_sysctl_save_ip6_properties(NMDevice *self)
         "disable_ipv6",
         "hop_limit",
         "use_tempaddr",
+        "temp_valid_lft",
+        "temp_prefered_lft",
     };
     NMDevicePrivate *priv     = NM_DEVICE_GET_PRIVATE(self);
     NMPlatform      *platform = nm_device_get_platform(self);
@@ -12476,6 +12626,17 @@ _dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode)
         }
     }
 
+    nm_device_sysctl_ip_conf_set(
+        self,
+        AF_INET6,
+        "temp_valid_lft",
+        nm_sprintf_buf(sbuf, "%u", (unsigned) _prop_get_ipv6_temp_valid_lifetime(self)));
+    nm_device_sysctl_ip_conf_set(
+        self,
+        AF_INET6,
+        "temp_prefered_lft",
+        nm_sprintf_buf(sbuf, "%u", (unsigned) _prop_get_ipv6_temp_preferred_lifetime(self)));
+
     if (addr_gen_mode == NM_IN6_ADDR_GEN_MODE_NONE) {
         gs_free char *value = NULL;
 
@@ -12536,7 +12697,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const
     ip_ifindex = nm_device_get_ip_ifindex(self);
 
     if (connection_ip_method_requires_carrier(connection, addr_family, NULL)
-        && nm_device_is_master(self) && !priv->carrier) {
+        && nm_device_is_controller(self) && !priv->carrier) {
         if (!priv->ip_data_x[IS_IPv4].wait_for_carrier) {
             _LOGT_ip(addr_family, "waiting until carrier is on");
             priv->ip_data_x[IS_IPv4].wait_for_carrier = TRUE;
@@ -12548,7 +12709,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const
         priv->ip_data_x[IS_IPv4].wait_for_carrier = FALSE;
     }
 
-    if (nm_device_is_master(self) && ip_requires_slaves(self, addr_family)) {
+    if (nm_device_is_controller(self) && ip_requires_slaves(self, addr_family)) {
         /* If the master has no ready slaves, and depends on slaves for
          * a successful IP configuration attempt, then postpone IP addressing.
          */
@@ -12595,7 +12756,7 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const
         } else if (nm_streq(method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
             if (!priv->ip_data_x[IS_IPv4].is_ignore) {
                 priv->ip_data_x[IS_IPv4].is_ignore = TRUE;
-                if (priv->master) {
+                if (priv->controller) {
                     /* If a device only has an IPv6 link-local address,
                      * we don't generate an assumed connection. Therefore,
                      * when a new slave connection (without IP configuration)
@@ -14959,8 +15120,39 @@ _unmanaged_flags2str(NMUnmanagedFlags flags, NMUnmanagedFlags mask, char *buf, g
     return buf;
 }
 
+static NMDeviceStateReason
+unmanaged_flags_to_reason(NMUnmanagedFlags flags)
+{
+    /* Even if there are multiple flags, we can only return one reason.
+     * Return the most important reason.
+     */
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_SLEEPING))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_QUITTING))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_SETTINGS))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_PLATFORM_INIT))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_UDEV))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_EXPLICIT))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_USER_CONF))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_BY_DEFAULT))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT;
+    if (NM_FLAGS_HAS(flags, NM_UNMANAGED_EXTERNAL_DOWN))
+        return NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN;
+
+    return NM_DEVICE_STATE_REASON_NOW_UNMANAGED;
+}
+
 static gboolean
-_get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean for_user_request)
+_get_managed_by_flags(NMUnmanagedFlags     flags,
+                      NMUnmanagedFlags     mask,
+                      gboolean             for_user_request,
+                      NMDeviceStateReason *unmanaged_reason)
 {
     /* Evaluate the managed state based on the unmanaged flags.
      *
@@ -14979,7 +15171,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo
          *
          * Effectively, this check is redundant, as the code below already
          * already ensures that. Still, express this invariant explicitly here. */
-        if (_get_managed_by_flags(flags, mask, FALSE))
+        if (_get_managed_by_flags(flags, mask, FALSE, unmanaged_reason))
             return TRUE;
 
         /* A for-user-request, is effectively the same as pretending
@@ -15022,7 +15214,12 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo
                    | NM_UNMANAGED_EXTERNAL_DOWN);
     }
 
-    return flags == NM_UNMANAGED_NONE;
+    if (flags == NM_UNMANAGED_NONE) {
+        return TRUE;
+    } else {
+        NM_SET_OUT(unmanaged_reason, unmanaged_flags_to_reason(flags));
+        return FALSE;
+    }
 }
 
 /**
@@ -15051,7 +15248,10 @@ nm_device_get_managed(NMDevice *self, gboolean for_user_request)
 
     priv = NM_DEVICE_GET_PRIVATE(self);
 
-    return _get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, for_user_request);
+    return _get_managed_by_flags(priv->unmanaged_flags,
+                                 priv->unmanaged_mask,
+                                 for_user_request,
+                                 NULL);
 }
 
 /**
@@ -15190,9 +15390,9 @@ _set_unmanaged_flags(NMDevice           *self,
           (priv->unmanaged_flags | priv->unmanaged_mask) ? "=" : "",
           (guint) priv->unmanaged_flags,
           (guint) priv->unmanaged_mask,
-          (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE)
+          (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE, NULL)
                ? "managed"
-               : (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, TRUE)
+               : (_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, TRUE, NULL)
                       ? "manageable"
                       : "unmanaged")),
           priv->real ? "" : "/unrealized",
@@ -15211,6 +15411,9 @@ _set_unmanaged_flags(NMDevice           *self,
     if (transition_state) {
         new_state = was_managed ? NM_DEVICE_STATE_UNMANAGED : NM_DEVICE_STATE_UNAVAILABLE;
         if (new_state == NM_DEVICE_STATE_UNMANAGED) {
+            /* In state UNMANAGED, the reason always depends on current flags, not on what
+             * the caller passed. */
+            _get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE, &reason);
             _cancel_activation(self);
         } else {
             /* The assume check should happen before the device transitions to
@@ -15225,6 +15428,13 @@ _set_unmanaged_flags(NMDevice           *self,
             nm_device_state_changed(self, new_state, reason);
         else
             nm_device_queue_state(self, new_state, reason);
+    } else {
+        /* No state change, but possibly update the reason in UNMANAGED */
+        if (!_get_managed_by_flags(priv->unmanaged_flags, priv->unmanaged_mask, FALSE, &reason)
+            && reason != priv->state_reason) {
+            priv->state_reason = reason;
+            _notify(self, PROP_STATE_REASON);
+        }
     }
 }
 
@@ -15483,7 +15693,7 @@ nm_device_update_firewall_zone(NMDevice *self)
 void
 nm_device_update_metered(NMDevice *self)
 {
-#define NM_METERED_INVALID ((NMMetered) -1)
+#define NM_METERED_INVALID ((NMMetered) - 1)
     NMDevicePrivate     *priv = NM_DEVICE_GET_PRIVATE(self);
     NMSettingConnection *setting;
     NMMetered            conn_value, value = NM_METERED_INVALID;
@@ -15773,7 +15983,7 @@ check_connection_available(NMDevice                      *self,
         return TRUE;
     }
 
-    if (nm_device_is_master(self)) {
+    if (nm_device_is_controller(self)) {
         /* master types are always available even without carrier.
          * Making connection non-available would un-enslave slaves which
          * is not desired. */
@@ -16164,12 +16374,12 @@ _cleanup_generic_post(NMDevice *self, NMDeviceStateReason reason, CleanupType cl
         nm_active_connection_set_default(NM_ACTIVE_CONNECTION(priv->act_request.obj),
                                          AF_INET,
                                          FALSE);
-        nm_clear_g_signal_handler(priv->act_request.obj, &priv->master_ready_id);
+        nm_clear_g_signal_handler(priv->act_request.obj, &priv->controller_ready_id);
         act_request_set(self, NULL);
     }
 
     if (cleanup_type == CLEANUP_TYPE_DECONFIGURE
-        && ((reason == NM_DEVICE_STATE_REASON_CARRIER && nm_device_is_master(self))
+        && ((reason == NM_DEVICE_STATE_REASON_CARRIER && nm_device_is_controller(self))
             || !NM_IN_SET(reason,
                           NM_DEVICE_STATE_REASON_NOW_MANAGED,
                           NM_DEVICE_STATE_REASON_CARRIER))) {
@@ -16253,9 +16463,9 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
         nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self), ifindex, NULL);
 
     /* slave: mark no longer enslaved */
-    if (priv->master && priv->ifindex > 0
+    if (priv->controller && priv->ifindex > 0
         && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0) {
-        nm_device_master_release_slave(priv->master,
+        nm_device_master_release_slave(priv->controller,
                                        self,
                                        RELEASE_SLAVE_TYPE_NO_CONFIG,
                                        NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
@@ -16666,6 +16876,10 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
             _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, FALSE);
         }
         break;
+    case NM_DEVICE_STATE_DEACTIVATING:
+        /* If we are now deactivating we should enforce IP cleanup. */
+        _cleanup_ip_pre(self, AF_INET, CLEANUP_TYPE_DECONFIGURE, FALSE);
+        _cleanup_ip_pre(self, AF_INET6, CLEANUP_TYPE_DECONFIGURE, FALSE);
     default:
         break;
     }
@@ -17431,6 +17645,8 @@ nm_device_hw_addr_set(NMDevice *self, const char *addr, const char *detail, gboo
  * @hwaddr: (out): the cloned MAC address to set on interface
  * @hwaddr_type: (out): the type of address to set
  * @hwaddr_detail: (out): the detail (origin) of address to set
+ * @is_default_special (out): if %TRUE, the cloned MAC comes from
+ *   global default configuration and is a special keyword
  * @error: on return, an error or %NULL
  *
  * Computes the MAC to be set on a interface. On success, one of the
@@ -17451,6 +17667,7 @@ _hw_addr_get_cloned(NMDevice     *self,
                     char        **hwaddr,
                     HwAddrType   *hwaddr_type,
                     const char  **hwaddr_detail,
+                    gboolean     *is_default_special,
                     GError      **error)
 {
     NMDevicePrivate *priv;
@@ -17469,7 +17686,7 @@ _hw_addr_get_cloned(NMDevice     *self,
     if (!connection)
         g_return_val_if_reached(FALSE);
 
-    addr_setting = _prop_get_x_cloned_mac_address(self, connection, is_wifi);
+    addr_setting = _prop_get_x_cloned_mac_address(self, connection, is_wifi, is_default_special);
 
     addr = addr_setting;
 
@@ -17605,7 +17822,7 @@ nm_device_hw_addr_get_cloned(NMDevice     *self,
                              gboolean     *preserve,
                              GError      **error)
 {
-    if (!_hw_addr_get_cloned(self, connection, is_wifi, preserve, hwaddr, NULL, NULL, error))
+    if (!_hw_addr_get_cloned(self, connection, is_wifi, preserve, hwaddr, NULL, NULL, NULL, error))
         return FALSE;
 
     return TRUE;
@@ -17615,11 +17832,13 @@ gboolean
 nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean is_wifi)
 {
     NMDevicePrivate      *priv;
-    gboolean              preserve = FALSE;
-    gs_free char         *hwaddr   = NULL;
-    const char           *detail   = NULL;
-    HwAddrType            type     = HW_ADDR_TYPE_UNSET;
-    gs_free_error GError *error    = NULL;
+    gboolean              preserve           = FALSE;
+    gs_free char         *hwaddr             = NULL;
+    const char           *detail             = NULL;
+    HwAddrType            type               = HW_ADDR_TYPE_UNSET;
+    gs_free_error GError *error              = NULL;
+    gboolean              is_default_special = FALSE;
+    gboolean              ret;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
     priv = NM_DEVICE_GET_PRIVATE(self);
@@ -17631,6 +17850,7 @@ nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean
                              &hwaddr,
                              &type,
                              &detail,
+                             &is_default_special,
                              &error)) {
         _LOGW(LOGD_DEVICE, "set-hw-addr: %s", error->message);
         return FALSE;
@@ -17640,8 +17860,22 @@ nm_device_hw_addr_set_cloned(NMDevice *self, NMConnection *connection, gboolean
         return nm_device_hw_addr_reset(self, detail);
 
     if (hwaddr) {
+        ret = _hw_addr_set(self, hwaddr, "set-cloned", detail);
+        if (!ret && is_default_special) {
+            /* If the distro sets a global special value for the cloned MAC (for
+             * example, "stable-ssid") and the driver doesn't support changing the
+             * MAC, all activations will fail on the interface unless users know
+             * that they need to change the cloned MAC. Be more tolerant to errors
+             * in case the MAC is global and special.
+             */
+            _LOGE(LOGD_DEVICE,
+                  "ignore error changing the MAC address to globally configured value \"%s\","
+                  "the device does not support it",
+                  detail);
+            return TRUE;
+        }
         priv->hw_addr_type = type;
-        return _hw_addr_set(self, hwaddr, "set-cloned", detail);
+        return ret;
     }
 
     return TRUE;
@@ -18283,8 +18517,8 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_PHYSICAL_PORT_ID:
         g_value_set_string(value, priv->physical_port_id);
         break;
-    case PROP_MASTER:
-        g_value_set_object(value, nm_device_get_master(self));
+    case PROP_CONTROLLER:
+        g_value_set_object(value, nm_device_get_controller(self));
         break;
     case PROP_PARENT:
         g_value_set_string(value, nm_dbus_track_obj_path_get(&priv->parent_device));
@@ -18602,7 +18836,7 @@ dispose(GObject *object)
 
     _cleanup_generic_post(self, NM_DEVICE_STATE_REASON_NONE, CLEANUP_TYPE_KEEP);
 
-    nm_assert(priv->master_ready_id == 0);
+    nm_assert(priv->controller_ready_id == 0);
 
     g_hash_table_remove_all(priv->ip6_saved_properties);
 
@@ -19052,11 +19286,12 @@ nm_device_class_init(NMDeviceClass *klass)
                             "",
                             NULL,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-    obj_properties[PROP_MASTER] = g_param_spec_object(NM_DEVICE_MASTER,
-                                                      "",
-                                                      "",
-                                                      NM_TYPE_DEVICE,
-                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+    obj_properties[PROP_CONTROLLER] =
+        g_param_spec_object(NM_DEVICE_CONTROLLER,
+                            "",
+                            "",
+                            NM_TYPE_DEVICE,
+                            G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
     obj_properties[PROP_PARENT] = g_param_spec_string(NM_DEVICE_PARENT,
                                                       "",
                                                       "",
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index b096d23a..ffe6b1af 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -66,7 +66,7 @@
 
 #define NM_DEVICE_TYPE_DESC          "type-desc"          /* Internal only */
 #define NM_DEVICE_IFINDEX            "ifindex"            /* Internal only */
-#define NM_DEVICE_MASTER             "master"             /* Internal only */
+#define NM_DEVICE_CONTROLLER         "controller"         /* Internal only */
 #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */
 
 /* Internal signals */
@@ -186,9 +186,9 @@ typedef struct _NMDeviceClass {
      * a delta in the MTU allowed value due the encapsulation overhead */
     guint16 mtu_parent_delta;
 
-    /* Whether the device type is a master-type. This depends purely on the
+    /* Whether the device type is a controller-type. This depends purely on the
      * type (NMDeviceClass), not the actual device instance. */
-    bool is_master : 1;
+    bool is_controller : 1;
 
     /* Force setting the MTU actually means first setting the MTU
      * to (desired_MTU-1) and then setting the desired_MTU
@@ -502,11 +502,11 @@ gboolean  nm_device_parent_notify_changed(NMDevice *self,
 const char *nm_device_parent_find_for_connection(NMDevice   *self,
                                                  const char *current_setting_parent);
 
-/* Master */
-gboolean nm_device_is_master(NMDevice *dev);
+/* Controller */
+gboolean nm_device_is_controller(NMDevice *dev);
 
-/* Slave */
-NMDevice *nm_device_get_master(NMDevice *dev);
+/* Port */
+NMDevice *nm_device_get_controller(NMDevice *dev);
 
 NMActRequest          *nm_device_get_act_request(NMDevice *dev);
 NMSettingsConnection  *nm_device_get_settings_connection(NMDevice *dev);
@@ -550,6 +550,7 @@ gboolean nm_device_check_connection_compatible(NMDevice     *device,
                                                GError      **error);
 
 gboolean nm_device_check_slave_connection_compatible(NMDevice *device, NMConnection *connection);
+gboolean nm_device_can_be_parent(NMDevice *device);
 
 gboolean nm_device_can_assume_connections(NMDevice *self);
 gboolean nm_device_unmanage_on_quit(NMDevice *self);
diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c
index 3bc03ac1..27d04003 100644
--- a/src/core/devices/ovs/nm-device-ovs-bridge.c
+++ b/src/core/devices/ovs/nm-device-ovs-bridge.c
@@ -171,7 +171,7 @@ nm_device_ovs_bridge_class_init(NMDeviceOvsBridgeClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES();
 
-    device_class->is_master                           = TRUE;
+    device_class->is_controller                       = TRUE;
     device_class->get_type_description                = get_type_description;
     device_class->create_and_realize                  = create_and_realize;
     device_class->unrealize                           = unrealize;
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 17eb2c2d..778f230b 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -355,12 +355,12 @@ ovs_interface_is_netdev_datapath(NMDeviceOvsInterface *self)
         return FALSE;
 
     /* get ovs-port active-connection */
-    ac = nm_active_connection_get_master(ac);
+    ac = nm_active_connection_get_controller(ac);
     if (!ac)
         return FALSE;
 
     /* get ovs-bridge active-connection */
-    ac = nm_active_connection_get_master(ac);
+    ac = nm_active_connection_get_controller(ac);
     if (!ac)
         return FALSE;
 
diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c
index 258c72f2..e1020280 100644
--- a/src/core/devices/ovs/nm-device-ovs-port.c
+++ b/src/core/devices/ovs/nm-device-ovs-port.c
@@ -160,7 +160,7 @@ attach_port(NMDevice                  *device,
         return TRUE;
 
     ac_port   = NM_ACTIVE_CONNECTION(nm_device_get_act_request(device));
-    ac_bridge = nm_active_connection_get_master(ac_port);
+    ac_bridge = nm_active_connection_get_controller(ac_port);
     if (!ac_bridge) {
         _LOGW(LOGD_DEVICE,
               "can't attach %s: bridge active-connection not found",
@@ -285,7 +285,7 @@ nm_device_ovs_port_class_init(NMDeviceOvsPortClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_PORT_SETTING_NAME;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES();
 
-    device_class->is_master                           = TRUE;
+    device_class->is_controller                       = TRUE;
     device_class->get_type_description                = get_type_description;
     device_class->create_and_realize                  = create_and_realize;
     device_class->get_generic_capabilities            = get_generic_capabilities;
diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c
index d8c3c2c2..1f70537f 100644
--- a/src/core/devices/team/nm-device-team.c
+++ b/src/core/devices/team/nm-device-team.c
@@ -1006,7 +1006,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 static void
 nm_device_team_init(NMDeviceTeam *self)
 {
-    nm_assert(nm_device_is_master(NM_DEVICE(self)));
+    nm_assert(nm_device_is_controller(NM_DEVICE(self)));
 }
 
 static void
@@ -1127,7 +1127,7 @@ nm_device_team_class_init(NMDeviceTeamClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_TEAM_SETTING_NAME;
     device_class->link_types                       = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_TEAM);
 
-    device_class->is_master                      = TRUE;
+    device_class->is_controller                  = TRUE;
     device_class->create_and_realize             = create_and_realize;
     device_class->get_generic_capabilities       = get_generic_capabilities;
     device_class->complete_connection            = complete_connection;
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 47407a1e..89444640 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -774,8 +774,8 @@ check_connection_compatible(NMDevice     *device,
             return FALSE;
         }
 
-        /* Check for MAC address blacklist */
-        mac_blacklist = nm_setting_wireless_get_mac_address_blacklist(s_wireless);
+        /* Check for MAC address denylist */
+        mac_blacklist = nm_setting_wireless_get_mac_address_denylist(s_wireless);
         for (i = 0; mac_blacklist[i]; i++) {
             nm_assert(nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN));
 
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index 2cd41b27..206113e4 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -1013,8 +1013,8 @@ check_connection_compatible(NMDevice     *device,
             return FALSE;
         }
 
-        /* Check for MAC address blacklist */
-        mac_blacklist = nm_setting_wireless_get_mac_address_blacklist(s_wireless);
+        /* Check for MAC address denylist */
+        mac_blacklist = nm_setting_wireless_get_mac_address_denylist(s_wireless);
         for (i = 0; mac_blacklist[i]; i++) {
             if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) {
                 g_warn_if_reached();
@@ -3374,7 +3374,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
     /* Tell the supplicant in which bridge the interface is */
     if ((request = nm_device_get_act_request(device))
-        && (master_ac = nm_active_connection_get_master(NM_ACTIVE_CONNECTION(request)))
+        && (master_ac = nm_active_connection_get_controller(NM_ACTIVE_CONNECTION(request)))
         && (master = nm_active_connection_get_device(master_ac))
         && nm_device_get_device_type(master) == NM_DEVICE_TYPE_BRIDGE) {
         nm_supplicant_interface_set_bridge(priv->sup_iface, nm_device_get_iface(master));
diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c
index d4d3815e..ceb954b7 100644
--- a/src/core/devices/wifi/nm-wifi-ap.c
+++ b/src/core/devices/wifi/nm-wifi-ap.c
@@ -719,13 +719,14 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_uchar(value, priv->strength);
         break;
     case PROP_LAST_SEEN:
-        g_value_set_int(value,
-                        priv->last_seen_msec != G_MININT64 ? (int) NM_MAX(
-                            nm_utils_monotonic_timestamp_as_boottime(priv->last_seen_msec,
-                                                                     NM_UTILS_NSEC_PER_MSEC)
-                                / 1000,
-                            1)
-                                                           : -1);
+        g_value_set_int(
+            value,
+            priv->last_seen_msec != G_MININT64
+                ? (int) NM_MAX(nm_utils_monotonic_timestamp_as_boottime(priv->last_seen_msec,
+                                                                        NM_UTILS_NSEC_PER_MSEC)
+                                   / 1000,
+                               1)
+                : -1);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
diff --git a/src/core/devices/wwan/meson.build b/src/core/devices/wwan/meson.build
index 37ef738c..acccb5fd 100644
--- a/src/core/devices/wwan/meson.build
+++ b/src/core/devices/wwan/meson.build
@@ -4,7 +4,7 @@ wwan_inc = include_directories('.')
 
 linker_script = join_paths(meson.current_source_dir(), 'libnm-wwan.ver')
 
-libnm_wwan = shared_module(
+libnm_wwan = shared_library(
   'nm-wwan',
   sources: files(
   'nm-service-providers.c',
@@ -21,6 +21,7 @@ libnm_wwan = shared_module(
   link_depends: linker_script,
   install: true,
   install_dir: nm_plugindir,
+  override_options: ['b_lundef=false'],
 )
 
 libnm_wwan_dep = declare_dependency(
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index a150040f..c03446b9 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -1155,6 +1155,8 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho
 #endif
     } else {
         NMPlatformIP6Address address;
+        NMPlatformIP6Address gw;
+        const char          *gw_string;
 
         address_string = mm_bearer_ip_config_get_address(self->_priv.ipv6_config);
         if (!address_string) {
@@ -1165,20 +1167,8 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho
                             NM_DEVICE_ERROR_INVALID_CONNECTION,
                             "(%s) retrieving IPv6 configuration failed: no address given",
                             nm_modem_get_uid(NM_MODEM(self)));
+                goto out;
             }
-            goto out;
-        }
-
-        address = (NMPlatformIP6Address){};
-
-        if (!inet_pton(AF_INET6, address_string, &address.address)) {
-            g_set_error(&error,
-                        NM_DEVICE_ERROR,
-                        NM_DEVICE_ERROR_INVALID_CONNECTION,
-                        "(%s) retrieving IPv6 configuration failed: invalid address given '%s'",
-                        nm_modem_get_uid(NM_MODEM(self)),
-                        address_string);
-            goto out;
         }
 
         data_port = mm_bearer_get_interface(self->_priv.bearer);
@@ -1202,43 +1192,57 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho
                                      NM_IP_CONFIG_SOURCE_WWAN);
         do_auto = TRUE;
 
-        address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv6_config);
-        if (address.plen <= 128) {
-            if (IN6_IS_ADDR_LINKLOCAL(&address.address)) {
-                nm_utils_ipv6_interface_identifier_get_from_addr(&iid_data, &address.address);
-                iid = &iid_data;
-            } else
-                do_auto = FALSE;
-            nm_l3_config_data_add_address_6(l3cd, &address);
+        if (address_string) {
+            address = (NMPlatformIP6Address){};
+
+            if (!inet_pton(AF_INET6, address_string, &address.address)) {
+                g_set_error(&error,
+                            NM_DEVICE_ERROR,
+                            NM_DEVICE_ERROR_INVALID_CONNECTION,
+                            "(%s) retrieving IPv6 configuration failed: invalid address given '%s'",
+                            nm_modem_get_uid(NM_MODEM(self)),
+                            address_string);
+                goto out;
+            }
+
+            address.plen = mm_bearer_ip_config_get_prefix(self->_priv.ipv6_config);
+            if (address.plen <= 128) {
+                if (IN6_IS_ADDR_LINKLOCAL(&address.address)) {
+                    nm_utils_ipv6_interface_identifier_get_from_addr(&iid_data, &address.address);
+                    iid = &iid_data;
+                } else
+                    do_auto = FALSE;
+                nm_l3_config_data_add_address_6(l3cd, &address);
+            }
+
+            _LOGI("  address %s", nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf)));
         }
 
-        _LOGI("  address %s (slaac %s)",
-              nm_platform_ip6_address_to_string(&address, sbuf, sizeof(sbuf)),
-              do_auto ? "enabled" : "disabled");
+        _LOGI("  slaac %s", do_auto ? "enabled" : "disabled");
 
-        address_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv6_config);
-        if (address_string) {
-            if (inet_pton(AF_INET6, address_string, &address.address) != 1) {
+        gw_string = mm_bearer_ip_config_get_gateway(self->_priv.ipv6_config);
+        if (gw_string) {
+            if (inet_pton(AF_INET6, gw_string, &gw.address) != 1) {
                 g_set_error(&error,
                             NM_DEVICE_ERROR,
                             NM_DEVICE_ERROR_INVALID_CONNECTION,
                             "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'",
                             nm_modem_get_uid(NM_MODEM(self)),
-                            address_string);
+                            gw_string);
                 goto out;
             }
 
             {
                 const NMPlatformIP6Route r = {
                     .rt_source     = NM_IP_CONFIG_SOURCE_WWAN,
-                    .gateway       = address.address,
+                    .gateway       = gw.address,
                     .table_any     = TRUE,
                     .table_coerced = 0,
                     .metric_any    = TRUE,
                     .metric        = 0,
                 };
 
-                _LOGI("  gateway %s", address_string);
+                _LOGI("  gateway %s", gw_string);
                 nm_l3_config_data_add_route_6(l3cd, &r);
             }
         } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) {
diff --git a/src/core/dhcp/README.next.md b/src/core/dhcp/README.next.md
new file mode 100644
index 00000000..88fa6683
--- /dev/null
+++ b/src/core/dhcp/README.next.md
@@ -0,0 +1,103 @@
+`NMDhcpClient`
+==============
+
+Using `NMDhcpClient` still requires a lot of logic in `NMDevice`. The main goal
+is to simplify `NMDevice`, so `NMDhcpClient` must become more complicated to
+provide a simpler (but robust) API.
+
+NMDevice has basically two timeouts (talking about IPv4, but it applies
+similarly to IPv6): `ipv4.dhcp-timeout` and `ipv4.required-timeout`. They
+control how long NMDevice is willing to try, before failing the activation
+altogether. Note that with `ipv4.may-fail=yes`, we may very well never want to
+fail the activation entirely, regardless how DHCP is doing. In that case we
+want to stay up, but also constantly retrying whether we cannot get a lease and
+recover.
+
+Currently, if `NMDhcpClient` signals a failure, then it's basically up to
+`NMDevice` to schedule and retry. That is complicated, and we should move the
+complexity out of `NMDevice`.
+
+`NMDhcpClient` should have a simpler API:
+
+- `nm_dhcp_manager_start_ip[46]()`: creates (and starts) a `NMDhcpClient`
+  instance. The difference is, this function tries really hard not to fail
+  to create an `NMDhcpClient`. There is no explicit `start()`, but note that the
+  instance must not emit any signals before the next maincontext iteration. That is,
+  it only will call back the user after a timeout/idle or some other IO event, which
+  happens during a future iteration of the maincontext.
+
+- `nm_dhcp_client_stop()`: when `NMDevice` is done with the `NMDhcpClient`
+  instance, it will stop it and throw it away. This method exists because
+  `NMDhcpClient` is a `GObject` and ref-counted. Thus, we don't want to rely on
+  the last unref to stop the instance, but have an explicit stop. After stop, the
+  instance is defunct and won't emit any signals anymore. The class does not need
+  to support restarting a stopped instance. If `NMDevice` wants to restart DHCP, it
+  should create a new one. `NMDevice` would only want to do that, if the parameters
+  change, hence a new instance is in order (and no need for the complexity of
+  restart in `NMDhcpClient`).
+
+- as already now, `NMDhcpClient` is not very configurable. You provide most
+  (all) parameters during `nm_dhcp_manager_start_ip[46]()`, and then it keeps
+  running until stop.
+
+- `NMDhcpClient` exposes a simple state to the user:
+
+   1. "no lease, but good". When starting, there is no lease, but we are
+      optimistic to get one. This is the inital state, but we can also get back to
+      this state after we had a lease (which might expire).
+
+   1. "has a lease". Here there is no need to distinguish whether the current
+      lease was the first we received, or whether this was an update. In this state,
+      the instance has a lease and we are good.
+
+   1. "no lease, but bad". `NMDhcpClient` tries really hard, and "bad" does not
+      mean that it gave up. It will keep retrying, it's just that there is little
+      hope of getting a new lease. This happens, when you try to run DHCP on a Layer3
+      link (WireGuard). There is little hope to succeed, but `NMDhcpClient`
+      (theoretically) will retry and may recover from this. Another example is when
+      we fail to start dhclient because it's not installed. In that case, we are not
+      optimistic to recover, however `NMDhcpDhclient` will retry (with backoff
+      timeout) and might still recover from this. For most cases, `NMDevice` will
+      treat the no-lease cases the same, but in case of "bad" it might give up
+      earlier.
+
+When a lease expires, that does not necessarily mean that we are now in a bad
+state. It might mean that the DHCP server is temporarily down, but we might
+recover from that easily. "bad" really means, something is wrong on our side
+which prevents us from getting a lease. Also, imagine `dhclient` dies (we would
+try to restart, but assume that fails too), but we still have a valid lease,
+then possibly `NMDhcpClient` should still pretend all is good and we still have
+a lease until it expires. It may be we can recover before that happens. The
+point of all of this, is to hide errors as much as possibly and automatically
+recover. `NMDevice` will decide to tear down, if we didn't get a lease after
+`ipv4.dhcp-timeout`. That's the main criteria, and it might not even
+distinguish between "no lease, but good" and "no lease, but bad".
+
+- `NMDhcpClient` will also take care of the `ipv4.dhcp-timeout` grace period.
+  That timeout is provided during start, and starts ticking whenever there is
+  no lease. When it expires, a timeout signal gets emitted. That's it. This is
+  independent from the 3 states above, and only saves `NMDevice` from scheduling
+  this timer themselves.
+  This is NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT notification.
+
+- for nettools, `nm_dhcp_client_can_accept()` indicates that when we receive a
+  lease, we need to accept/decline it first. In that case, `NMDevice`
+optionally does ACD first, then configures the IP address first and calls
+`nm_dhcp_client_accept()`. In case of ACD conflict, it will call
+`nm_dhcp_client_decline()` (which optimally causes `NMDhcpClient` to get a
+different lease). With this, the above state "has a lease" has actually three
+flavors: "has a lease but not yet ACD probed" and "has a lease but
+accepted/declined" (but `NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED` gets only emitted
+when we get the lease, not when we accept/decline it). With `dhclient`, when we
+receive a lease, it means  "has a lease but accepted" right away.
+
+- for IPv6 prefix delegation, there is also `needed_prefixes` and
+  `NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED`. Currently `needed_prefixes` needs
+  to be specified during start (which simplifies things). Maybe `needed_prefixes`
+  should be changable at runtime. Otherwise, whether we have prefixes is similar
+  to whether we have a lease, and the simple 3 states apply.
+
+When NetworkManager quits, it may want to leave the interface up. In that case,
+we still always want to stop the DHCP client, but possibly not deconfiguring
+the interface. I don't think that this concerns `NMDhcpClient`, because `NMDhcpClient`
+only provides the lease information and `NMDevice` is responsible to configure it.
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index 8770656b..4ebc1754 100644
--- a/src/core/dhcp/nm-dhcp-client.c
+++ b/src/core/dhcp/nm-dhcp-client.c
@@ -824,9 +824,10 @@ _nm_dhcp_client_notify(NMDhcpClient         *self,
 
     _acd_check_lease(self, &acd_state);
 
-    options = priv->l3cd_next ? nm_dhcp_lease_get_options(
-                  nm_l3_config_data_get_dhcp_lease(priv->l3cd_next, priv->config.addr_family))
-                              : NULL;
+    options = priv->l3cd_next
+                  ? nm_dhcp_lease_get_options(
+                        nm_l3_config_data_get_dhcp_lease(priv->l3cd_next, priv->config.addr_family))
+                  : NULL;
 
     if (_LOGI_ENABLED()) {
         const char *req_str =
diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c
index ce1e9a45..b81ce77a 100644
--- a/src/core/dhcp/nm-dhcp-nettools.c
+++ b/src/core/dhcp/nm-dhcp-nettools.c
@@ -1483,6 +1483,11 @@ stop(NMDhcpClient *client, gboolean release)
     NMDhcpNettools        *self = NM_DHCP_NETTOOLS(client);
     NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE(self);
 
+    if (release) {
+        if (n_dhcp4_client_probe_release(priv->probe))
+            _LOGT("dhcp-client4: failed to send request with RELEASE message");
+    }
+
     NM_DHCP_CLIENT_CLASS(nm_dhcp_nettools_parent_class)->stop(client, release);
 
     _LOGT("dhcp-client4: stop " NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(priv->client));
diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c
index 0fc5f928..5ede0df9 100644
--- a/src/core/dhcp/nm-dhcp-systemd.c
+++ b/src/core/dhcp/nm-dhcp-systemd.c
@@ -67,6 +67,15 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
 
 /*****************************************************************************/
 
+static guint32
+lifetime_to_uint32(guint64 lft)
+{
+    if (lft == G_MAXUINT64)
+        return G_MAXUINT32;
+
+    return lft / 1000000;
+}
+
 static NML3ConfigData *
 lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GError **error)
 {
@@ -100,18 +109,19 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
 
     if (!config->v6.info_only) {
         gboolean has_any_addresses = FALSE;
-        uint32_t lft_pref;
-        uint32_t lft_valid;
+        uint64_t lft_pref;
+        uint64_t lft_valid;
 
-        sd_dhcp6_lease_reset_address_iter(lease);
+        sd_dhcp6_lease_address_iterator_reset(lease);
         nm_gstring_prepare(&str);
-        while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
-            const NMPlatformIP6Address address = {
+        while (sd_dhcp6_lease_get_address(lease, &tmp_addr) >= 0
+               && sd_dhcp6_lease_get_address_lifetime(lease, &lft_pref, &lft_valid) >= 0) {
+            NMPlatformIP6Address address = {
                 .plen        = 128,
                 .address     = tmp_addr,
                 .timestamp   = ts,
-                .lifetime    = lft_valid,
-                .preferred   = lft_pref,
+                .lifetime    = lifetime_to_uint32(lft_valid),
+                .preferred   = lifetime_to_uint32(lft_pref),
                 .addr_source = NM_IP_CONFIG_SOURCE_DHCP,
             };
 
@@ -121,6 +131,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
             g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
 
             has_any_addresses = TRUE;
+            sd_dhcp6_lease_address_iterator_next(lease);
         }
 
         if (str->len) {
@@ -160,11 +171,12 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
         uint8_t         prefix_len;
 
         nm_gstring_prepare(&str);
-        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
-        while (!sd_dhcp6_lease_get_pd(lease, &prefix, &prefix_len, NULL, NULL)) {
+        sd_dhcp6_lease_pd_iterator_reset(lease);
+        while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix, &prefix_len)) {
             nm_gstring_add_space_delimiter(str);
             nm_inet6_ntop(&prefix, addr_str);
             g_string_append_printf(str, "%s/%u", addr_str, prefix_len);
+            sd_dhcp6_lease_pd_iterator_next(lease);
         }
         if (str->len > 0) {
             nm_dhcp_option_add_option(options,
@@ -235,6 +247,8 @@ bound6_handle(NMDhcpSystemd *self)
     gs_free_error GError                   *error  = NULL;
     NMPlatformIP6Address                    prefix = {0};
     sd_dhcp6_lease                         *lease  = NULL;
+    guint64                                 lft_valid;
+    guint64                                 lft_pref;
 
     if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) {
         _LOGW(" no lease!");
@@ -254,14 +268,14 @@ bound6_handle(NMDhcpSystemd *self)
 
     _nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_BOUND, l3cd);
 
-    sd_dhcp6_lease_reset_pd_prefix_iter(lease);
-    while (!sd_dhcp6_lease_get_pd(lease,
-                                  &prefix.address,
-                                  &prefix.plen,
-                                  &prefix.preferred,
-                                  &prefix.lifetime)) {
+    sd_dhcp6_lease_pd_iterator_reset(lease);
+    while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix.address, &prefix.plen)
+           && !sd_dhcp6_lease_get_pd_lifetime(lease, &lft_pref, &lft_valid)) {
+        prefix.preferred = lifetime_to_uint32(lft_pref);
+        prefix.lifetime  = lifetime_to_uint32(lft_valid);
         prefix.timestamp = ts;
         nm_dhcp_client_emit_ipv6_prefix_delegated(NM_DHCP_CLIENT(self), &prefix);
+        sd_dhcp6_lease_pd_iterator_next(lease);
     }
 }
 
@@ -339,10 +353,10 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
         return FALSE;
     }
 
-    r = sd_dhcp6_client_set_duid(sd_client,
-                                 unaligned_read_be16(&duid_arr[0]),
-                                 &duid_arr[2],
-                                 duid_len - 2);
+    r = sd_dhcp6_client_set_duid_raw(sd_client,
+                                     unaligned_read_be16(&duid_arr[0]),
+                                     &duid_arr[2],
+                                     duid_len - 2);
     if (r < 0) {
         nm_utils_error_set_errno(error, r, "failed to set DUID: %s");
         return FALSE;
@@ -450,6 +464,10 @@ stop(NMDhcpClient *client, gboolean release)
     if (!priv->client6)
         return;
 
+    r = sd_dhcp6_client_set_send_release(priv->client6, release);
+    if (r)
+        _LOGT("dhcp-client6: failed setting send-release");
+
     sd_dhcp6_client_set_callback(priv->client6, NULL, NULL);
     r = sd_dhcp6_client_stop(priv->client6);
     if (r)
diff --git a/src/core/meson.build b/src/core/meson.build
index 45b068a6..4419ff62 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -171,7 +171,7 @@ libNetworkManager = static_library(
     'nm-policy.c',
     'nm-rfkill-manager.c',
     'nm-session-monitor.c',
-    'nm-sleep-monitor.c',
+    'nm-power-monitor.c',
     'nm-priv-helper-call.c',
   ),
   dependencies: nm_deps,
diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c
index e6b1a94e..ca646a6d 100644
--- a/src/core/ndisc/nm-ndisc.c
+++ b/src/core/ndisc/nm-ndisc.c
@@ -1853,6 +1853,7 @@ _config_init(NMNDiscConfig *config, const NMNDiscConfig *src)
     g_return_if_fail(
         NM_IN_SET(config->node_type, NM_NDISC_NODE_TYPE_HOST, NM_NDISC_NODE_TYPE_ROUTER));
     g_return_if_fail(NM_IN_SET(config->ip6_privacy,
+                               NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
                                NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED,
                                NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR,
                                NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR));
diff --git a/src/core/ndisc/nm-ndisc.h b/src/core/ndisc/nm-ndisc.h
index 8f1a12a2..b8f8b06e 100644
--- a/src/core/ndisc/nm-ndisc.h
+++ b/src/core/ndisc/nm-ndisc.h
@@ -40,7 +40,7 @@ typedef enum {
 
 const char *nm_ndisc_dhcp_level_to_string(NMNDiscDHCPLevel level);
 
-#define NM_NDISC_INFINITY_U32 ((uint32_t) -1)
+#define NM_NDISC_INFINITY_U32 ((uint32_t) - 1)
 
 /* It's important that this is G_MAXINT64, so that we can meaningfully do
  * MIN(e1, e2) to find the minimum expiry time (and properly handle if any
diff --git a/src/core/nm-active-connection.c b/src/core/nm-active-connection.c
index eb7b1cca..b08d26c2 100644
--- a/src/core/nm-active-connection.c
+++ b/src/core/nm-active-connection.c
@@ -38,7 +38,7 @@ typedef struct _NMActiveConnectionPrivate {
     bool                    is_default6 : 1;
     bool                    state_set : 1;
     bool                    vpn : 1;
-    bool                    master_ready : 1;
+    bool                    controller_ready : 1;
 
     NMActivationType activation_type : 3;
 
@@ -49,7 +49,7 @@ typedef struct _NMActiveConnectionPrivate {
     NMActivationReason activation_reason : 4;
 
     NMAuthSubject      *subject;
-    NMActiveConnection *master;
+    NMActiveConnection *controller;
 
     NMActiveConnection *parent;
 
@@ -87,8 +87,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMActiveConnection,
                              PROP_INT_APPLIED_CONNECTION,
                              PROP_INT_DEVICE,
                              PROP_INT_SUBJECT,
-                             PROP_INT_MASTER,
-                             PROP_INT_MASTER_READY,
+                             PROP_INT_CONTROLLER,
+                             PROP_INT_CONTROLLER_READY,
                              PROP_INT_ACTIVATION_TYPE,
                              PROP_INT_ACTIVATION_REASON, );
 
@@ -105,7 +105,7 @@ G_DEFINE_ABSTRACT_TYPE(NMActiveConnection, nm_active_connection, NM_TYPE_DBUS_OB
 static const NMDBusInterfaceInfoExtended interface_info_active_connection;
 static const GDBusSignalInfo             signal_info_state_changed;
 
-static void check_master_ready(NMActiveConnection *self);
+static void check_controller_ready(NMActiveConnection *self);
 static void _device_cleanup(NMActiveConnection *self);
 static void _settings_connection_flags_changed(NMSettingsConnection *settings_connection,
                                                NMActiveConnection   *self);
@@ -276,7 +276,7 @@ nm_active_connection_set_state(NMActiveConnection           *self,
     emit_state_changed(self, new_state, reason);
     _notify(self, PROP_STATE);
 
-    check_master_ready(self);
+    check_controller_ready(self);
 
     if (new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
         || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
@@ -446,7 +446,7 @@ _set_applied_connection_take(NMActiveConnection *self, NMConnection *applied_con
     /* we determine whether the connection is a master/slave, based solely
      * on the connection properties itself. */
     s_con = nm_connection_get_setting_connection(priv->applied_connection);
-    if (nm_setting_connection_get_master(s_con))
+    if (nm_setting_connection_get_controller(s_con))
         flags_val |= NM_ACTIVATION_STATE_FLAG_IS_SLAVE;
 
     if (_nm_connection_type_is_master(nm_setting_connection_get_connection_type(s_con)))
@@ -650,13 +650,13 @@ device_master_changed(GObject *object, GParamSpec *pspec, gpointer user_data)
 
     if (NM_ACTIVE_CONNECTION(nm_device_get_act_request(device)) != self)
         return;
-    if (!nm_device_get_master(device))
+    if (!nm_device_get_controller(device))
         return;
-    if (!nm_active_connection_get_master(self))
+    if (!nm_active_connection_get_controller(self))
         return;
     g_signal_handlers_disconnect_by_func(device, G_CALLBACK(device_master_changed), self);
 
-    master       = nm_active_connection_get_master(self);
+    master       = nm_active_connection_get_controller(self);
     master_state = nm_active_connection_get_state(master);
     if (master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) {
         /* Master failed before attaching the slave */
@@ -704,15 +704,15 @@ nm_active_connection_set_device(NMActiveConnection *self, NMDevice *device)
 
     if (device) {
         /* Device obviously can't be its own master */
-        g_return_val_if_fail(!priv->master
-                                 || device != nm_active_connection_get_device(priv->master),
+        g_return_val_if_fail(!priv->controller
+                                 || device != nm_active_connection_get_device(priv->controller),
                              FALSE);
 
         priv->device = g_object_ref(device);
 
         g_signal_connect(device, NM_DEVICE_STATE_CHANGED, G_CALLBACK(device_state_changed), self);
         g_signal_connect(device,
-                         "notify::" NM_DEVICE_MASTER,
+                         "notify::" NM_DEVICE_CONTROLLER,
                          G_CALLBACK(device_master_changed),
                          self);
         g_signal_connect(device,
@@ -747,30 +747,30 @@ nm_active_connection_set_device(NMActiveConnection *self, NMDevice *device)
 }
 
 NMActiveConnection *
-nm_active_connection_get_master(NMActiveConnection *self)
+nm_active_connection_get_controller(NMActiveConnection *self)
 {
     g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(self), NULL);
 
-    return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->master;
+    return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->controller;
 }
 
 /**
- * nm_active_connection_get_master_ready:
+ * nm_active_connection_get_controller_ready:
  * @self: the #NMActiveConnection
  *
- * Returns: %TRUE if the connection has a master connection, and that
- * master connection is ready to accept slaves.  Otherwise, %FALSE.
+ * Returns: %TRUE if the connection has a controller connection, and that
+ * controller connection is ready to accept ports.  Otherwise, %FALSE.
  */
 gboolean
-nm_active_connection_get_master_ready(NMActiveConnection *self)
+nm_active_connection_get_controller_ready(NMActiveConnection *self)
 {
     g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(self), FALSE);
 
-    return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->master_ready;
+    return NM_ACTIVE_CONNECTION_GET_PRIVATE(self)->controller_ready;
 }
 
 static void
-check_master_ready(NMActiveConnection *self)
+check_controller_ready(NMActiveConnection *self)
 {
     NMActiveConnectionPrivate *priv       = NM_ACTIVE_CONNECTION_GET_PRIVATE(self);
     gboolean                   signalling = FALSE;
@@ -780,30 +780,31 @@ check_master_ready(NMActiveConnection *self)
      * device will be ready to accept slaves when the master is in ACTIVATING
      * or higher states.
      */
-    if (!priv->master_ready && priv->master && priv->state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
-        && NM_IN_SET(nm_active_connection_get_state(priv->master),
+    if (!priv->controller_ready && priv->controller
+        && priv->state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
+        && NM_IN_SET(nm_active_connection_get_state(priv->controller),
                      NM_ACTIVE_CONNECTION_STATE_ACTIVATING,
                      NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
         signalling = TRUE;
     }
 
-    _LOGD("check-master-ready: %s (state %s, %s)",
-          signalling ? "signal" : (priv->master_ready ? "already signalled" : "not signalling"),
+    _LOGD("check-controller-ready: %s (state %s, %s)",
+          signalling ? "signal" : (priv->controller_ready ? "already signalled" : "not signalling"),
           state_to_string_a(priv->state),
-          priv->master
+          priv->controller
               ? nm_sprintf_bufa(128,
-                                "master %p is in state %s",
-                                priv->master,
-                                state_to_string_a(nm_active_connection_get_state(priv->master)))
-              : "no master");
+                                "controller %p is in state %s",
+                                priv->controller,
+                                state_to_string_a(nm_active_connection_get_state(priv->controller)))
+              : "no controller");
 
     if (signalling) {
-        priv->master_ready = TRUE;
-        _notify(self, PROP_INT_MASTER_READY);
+        priv->controller_ready = TRUE;
+        _notify(self, PROP_INT_CONTROLLER_READY);
 
-        /* Also notify clients to recheck the exported 'master' property to
-         * ensure that if the master connection was created without a device
-         * that we notify clients when the master device is known.
+        /* Also notify clients to recheck the exported 'controller' property to
+         * ensure that if the controller connection was created without a device
+         * that we notify clients when the controller device is known.
          */
         nm_gobject_notify_together(self, PROP_MASTER, PROP_CONTROLLER);
     }
@@ -816,9 +817,9 @@ master_state_cb(NMActiveConnection *master, GParamSpec *pspec, gpointer user_dat
     NMActiveConnectionPrivate *priv         = NM_ACTIVE_CONNECTION_GET_PRIVATE(self);
     NMActiveConnectionState    master_state = nm_active_connection_get_state(master);
 
-    check_master_ready(self);
+    check_controller_ready(self);
 
-    if (master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING && !priv->master_ready) {
+    if (master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING && !priv->controller_ready) {
         /* Master disconnected before the slave was added */
         if (NM_ACTIVE_CONNECTION_GET_CLASS(self)->master_failed)
             NM_ACTIVE_CONNECTION_GET_CLASS(self)->master_failed(self);
@@ -826,44 +827,44 @@ master_state_cb(NMActiveConnection *master, GParamSpec *pspec, gpointer user_dat
 }
 
 /**
- * nm_active_connection_set_master:
+ * nm_active_connection_set_controller:
  * @self: the #NMActiveConnection
- * @master: if the activation depends on another device (ie, bond or bridge
- * master to which this device will be enslaved) pass the #NMActiveConnection
+ * @controller: if the activation depends on another device (ie, bond or bridge
+ * controller to which this device will be set as port) pass the #NMActiveConnection
  * that this activation request is a child of
  *
- * Sets the master active connection of @self.
+ * Sets the controller active connection of @self.
  */
 void
-nm_active_connection_set_master(NMActiveConnection *self, NMActiveConnection *master)
+nm_active_connection_set_controller(NMActiveConnection *self, NMActiveConnection *controller)
 {
     NMActiveConnectionPrivate *priv;
 
     g_return_if_fail(NM_IS_ACTIVE_CONNECTION(self));
-    g_return_if_fail(NM_IS_ACTIVE_CONNECTION(master));
+    g_return_if_fail(NM_IS_ACTIVE_CONNECTION(controller));
 
     priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self);
 
-    /* Master is write-once, and must be set before exporting the object */
-    g_return_if_fail(priv->master == NULL);
+    /* Controller is write-once, and must be set before exporting the object */
+    g_return_if_fail(priv->controller == NULL);
     g_return_if_fail(!nm_dbus_object_is_exported(NM_DBUS_OBJECT(self)));
     if (priv->device) {
         /* Note, the master ActiveConnection may not yet have a device */
-        g_return_if_fail(priv->device != nm_active_connection_get_device(master));
+        g_return_if_fail(priv->device != nm_active_connection_get_device(controller));
     }
 
-    _LOGD("set master %p, %s, state %s",
-          master,
-          nm_active_connection_get_settings_connection_id(master),
-          state_to_string_a(nm_active_connection_get_state(master)));
+    _LOGD("set controller %p, %s, state %s",
+          controller,
+          nm_active_connection_get_settings_connection_id(controller),
+          state_to_string_a(nm_active_connection_get_state(controller)));
 
-    priv->master = g_object_ref(master);
-    g_signal_connect(priv->master,
+    priv->controller = g_object_ref(controller);
+    g_signal_connect(priv->controller,
                      "notify::" NM_ACTIVE_CONNECTION_STATE,
                      G_CALLBACK(master_state_cb),
                      self);
 
-    check_master_ready(self);
+    check_controller_ready(self);
 }
 
 NMActivationType
@@ -1347,15 +1348,15 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         break;
     case PROP_CONTROLLER:
     case PROP_MASTER:
-        if (priv->master)
-            master_device = nm_active_connection_get_device(priv->master);
+        if (priv->controller)
+            master_device = nm_active_connection_get_device(priv->controller);
         nm_dbus_utils_g_value_set_object_path(value, master_device);
         break;
     case PROP_INT_SUBJECT:
         g_value_set_object(value, priv->subject);
         break;
-    case PROP_INT_MASTER_READY:
-        g_value_set_boolean(value, priv->master_ready);
+    case PROP_INT_CONTROLLER_READY:
+        g_value_set_boolean(value, priv->controller_ready);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -1401,8 +1402,8 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         /* construct-only */
         priv->subject = g_value_dup_object(value);
         break;
-    case PROP_INT_MASTER:
-        nm_active_connection_set_master(self, g_value_get_object(value));
+    case PROP_INT_CONTROLLER:
+        nm_active_connection_set_controller(self, g_value_get_object(value));
         break;
     case PROP_INT_ACTIVATION_TYPE:
         /* construct-only */
@@ -1529,10 +1530,10 @@ dispose(GObject *object)
 
     _device_cleanup(self);
 
-    if (priv->master) {
-        g_signal_handlers_disconnect_by_func(priv->master, G_CALLBACK(master_state_cb), self);
+    if (priv->controller) {
+        g_signal_handlers_disconnect_by_func(priv->controller, G_CALLBACK(master_state_cb), self);
     }
-    g_clear_object(&priv->master);
+    g_clear_object(&priv->controller);
 
     if (priv->parent)
         unwatch_parent(self, TRUE);
@@ -1774,15 +1775,15 @@ nm_active_connection_class_init(NMActiveConnectionClass *ac_class)
                             NM_TYPE_AUTH_SUBJECT,
                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
-    obj_properties[PROP_INT_MASTER] =
-        g_param_spec_object(NM_ACTIVE_CONNECTION_INT_MASTER,
+    obj_properties[PROP_INT_CONTROLLER] =
+        g_param_spec_object(NM_ACTIVE_CONNECTION_INT_CONTROLLER,
                             "",
                             "",
                             NM_TYPE_ACTIVE_CONNECTION,
                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
-    obj_properties[PROP_INT_MASTER_READY] =
-        g_param_spec_boolean(NM_ACTIVE_CONNECTION_INT_MASTER_READY,
+    obj_properties[PROP_INT_CONTROLLER_READY] =
+        g_param_spec_boolean(NM_ACTIVE_CONNECTION_INT_CONTROLLER_READY,
                              "",
                              "",
                              FALSE,
diff --git a/src/core/nm-active-connection.h b/src/core/nm-active-connection.h
index 8032294f..12cb311c 100644
--- a/src/core/nm-active-connection.h
+++ b/src/core/nm-active-connection.h
@@ -45,8 +45,8 @@
 #define NM_ACTIVE_CONNECTION_INT_APPLIED_CONNECTION  "int-applied-connection"
 #define NM_ACTIVE_CONNECTION_INT_DEVICE              "int-device"
 #define NM_ACTIVE_CONNECTION_INT_SUBJECT             "int-subject"
-#define NM_ACTIVE_CONNECTION_INT_MASTER              "int-master"
-#define NM_ACTIVE_CONNECTION_INT_MASTER_READY        "int-master-ready"
+#define NM_ACTIVE_CONNECTION_INT_CONTROLLER          "int-controller"
+#define NM_ACTIVE_CONNECTION_INT_CONTROLLER_READY    "int-controller-ready"
 #define NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE     "int-activation-type"
 #define NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON   "int-activation-reason"
 
@@ -170,11 +170,11 @@ NMAuthSubject *nm_active_connection_get_subject(NMActiveConnection *self);
 
 gboolean nm_active_connection_get_user_requested(NMActiveConnection *self);
 
-NMActiveConnection *nm_active_connection_get_master(NMActiveConnection *self);
+NMActiveConnection *nm_active_connection_get_controller(NMActiveConnection *self);
 
-gboolean nm_active_connection_get_master_ready(NMActiveConnection *self);
+gboolean nm_active_connection_get_controller_ready(NMActiveConnection *self);
 
-void nm_active_connection_set_master(NMActiveConnection *self, NMActiveConnection *master);
+void nm_active_connection_set_controller(NMActiveConnection *self, NMActiveConnection *controller);
 
 void nm_active_connection_set_parent(NMActiveConnection *self, NMActiveConnection *parent);
 
diff --git a/src/core/nm-audit-manager.c b/src/core/nm-audit-manager.c
index 4e134d1a..7cf52946 100644
--- a/src/core/nm-audit-manager.c
+++ b/src/core/nm-audit-manager.c
@@ -185,13 +185,16 @@ nm_audit_log(NMAuditManager *self,
     priv = NM_AUDIT_MANAGER_GET_PRIVATE(self);
 
     if (priv->auditd_fd >= 0) {
-        audit_log_user_message(priv->auditd_fd,
-                               AUDIT_USYS_CONFIG,
-                               build_message(&strbuf, BACKEND_AUDITD, fields),
-                               NULL,
-                               NULL,
-                               NULL,
-                               success);
+        int r;
+
+        r = audit_log_user_message(priv->auditd_fd,
+                                   AUDIT_USYS_CONFIG,
+                                   build_message(&strbuf, BACKEND_AUDITD, fields),
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   success);
+        (void) r;
     }
 #endif
 
diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c
index 74adf484..ffcf6e3a 100644
--- a/src/core/nm-checkpoint.c
+++ b/src/core/nm-checkpoint.c
@@ -10,12 +10,15 @@
 #include "nm-active-connection.h"
 #include "nm-act-request.h"
 #include "libnm-core-aux-intern/nm-auth-subject.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 #include "nm-core-utils.h"
 #include "nm-dbus-interface.h"
 #include "devices/nm-device.h"
+#include "nm-config.h"
 #include "nm-manager.h"
 #include "settings/nm-settings.h"
 #include "settings/nm-settings-connection.h"
+#include "settings/plugins/keyfile/nms-keyfile-storage.h"
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
 
@@ -28,11 +31,14 @@ typedef struct {
     NMDevice          *device;
     NMConnection      *applied_connection;
     NMConnection      *settings_connection;
+    NMConnection      *settings_connection_shadowed;
     guint64            ac_version_id;
     NMDeviceState      state;
     bool               is_software : 1;
     bool               realized : 1;
     bool               activation_lifetime_bound_to_profile_visibility : 1;
+    bool               settings_connection_is_unsaved : 1;
+    bool               settings_connection_is_shadowed_owned : 1;
     NMUnmanFlagOp      unmanaged_explicit;
     NMActivationReason activation_reason;
     gulong             dev_exported_change_id;
@@ -55,6 +61,8 @@ struct _NMCheckpointPrivate {
 
     NMCheckpointTimeoutCallback timeout_cb;
     gpointer                    timeout_data;
+
+    NMGlobalDnsConfig *global_dns_config;
 };
 
 struct _NMCheckpointClass {
@@ -147,37 +155,111 @@ nm_checkpoint_includes_devices_of(NMCheckpoint *self, NMCheckpoint *cp_for_devic
     return NULL;
 }
 
+static NMConnection *
+parse_connection_from_shadowed_file(const char *path, GError **error)
+{
+    nm_auto_unref_keyfile GKeyFile *keyfile  = NULL;
+    gs_free char                   *base_dir = NULL;
+    char                           *sep;
+
+    keyfile = g_key_file_new();
+    if (!g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, error))
+        return NULL;
+
+    sep      = strrchr(path, '/');
+    base_dir = g_strndup(path, sep - path);
+
+    return nm_keyfile_read(keyfile, base_dir, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, error);
+}
+
 static NMSettingsConnection *
-find_settings_connection(NMCheckpoint     *self,
-                         DeviceCheckpoint *dev_checkpoint,
-                         gboolean         *need_update,
-                         gboolean         *need_activation)
+find_settings_connection(NMCheckpoint                    *self,
+                         DeviceCheckpoint                *dev_checkpoint,
+                         gboolean                        *need_update,
+                         gboolean                        *need_update_shadowed,
+                         gboolean                        *need_activation,
+                         NMSettingsConnectionPersistMode *persist_mode)
 {
     NMCheckpointPrivate  *priv = NM_CHECKPOINT_GET_PRIVATE(self);
     NMActiveConnection   *active;
     NMSettingsConnection *sett_conn;
+    const char           *shadowed_file;
+    NMConnection         *shadowed_connection = NULL;
     const char           *uuid, *ac_uuid;
     const CList          *tmp_clist;
-
-    *need_activation = FALSE;
-    *need_update     = FALSE;
+    gboolean              sett_conn_unsaved;
+    NMSettingsStorage    *storage;
+
+    *need_activation      = FALSE;
+    *need_update          = FALSE;
+    *need_update_shadowed = FALSE;
+
+    /* With regard to storage, there are 4 different possible states for the settings
+     * connection: 1) persistent; 2) in-memory only; 3) in-memory shadowing a persistent
+     * file; 4) in-memory shadowing a detached persistent file (i.e. the deletion of
+     * the connection doesn't delete the persistent file).
+     */
+    if (dev_checkpoint->settings_connection_is_unsaved) {
+        if (dev_checkpoint->settings_connection_shadowed) {
+            if (dev_checkpoint->settings_connection_is_shadowed_owned)
+                *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
+            else
+                *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+        } else
+            *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+    } else {
+        *persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
+    }
 
     uuid      = nm_connection_get_uuid(dev_checkpoint->settings_connection);
     sett_conn = nm_settings_get_connection_by_uuid(NM_SETTINGS_GET, uuid);
 
-    if (!sett_conn)
-        return NULL;
-
-    /* Now check if the connection changed, ... */
-    if (!nm_connection_compare(dev_checkpoint->settings_connection,
-                               nm_settings_connection_get_connection(sett_conn),
-                               NM_SETTING_COMPARE_FLAG_EXACT)) {
+    /* Check if the connection changed */
+    if (sett_conn
+        && !nm_connection_compare(dev_checkpoint->settings_connection,
+                                  nm_settings_connection_get_connection(sett_conn),
+                                  NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)) {
         _LOGT("rollback: settings connection %s changed", uuid);
         *need_update     = TRUE;
         *need_activation = TRUE;
     }
 
-    /* ... is active, ... */
+    storage       = sett_conn ? nm_settings_connection_get_storage(sett_conn) : NULL;
+    shadowed_file = storage ? nm_settings_storage_get_shadowed_storage(storage, NULL) : NULL;
+    shadowed_connection =
+        shadowed_file ? parse_connection_from_shadowed_file(shadowed_file, NULL) : NULL;
+
+    if (dev_checkpoint->settings_connection_shadowed) {
+        if (!shadowed_connection
+            || !nm_connection_compare(dev_checkpoint->settings_connection_shadowed,
+                                      shadowed_connection,
+                                      NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)) {
+            _LOGT("rollback: shadowed connection changed for %s", uuid);
+            *need_update_shadowed = TRUE;
+            *need_update          = TRUE;
+        }
+    } else {
+        if (shadowed_connection) {
+            _LOGT("rollback: shadowed connection changed for %s", uuid);
+            *need_update = TRUE;
+        }
+    }
+
+    if (!sett_conn)
+        return NULL;
+
+    /* Check if the connection unsaved flag changed */
+    sett_conn_unsaved = NM_FLAGS_HAS(nm_settings_connection_get_flags(sett_conn),
+                                     NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+    if (sett_conn_unsaved != dev_checkpoint->settings_connection_is_unsaved) {
+        _LOGT("rollback: storage changed for settings connection %s: unsaved (%d -> %d)",
+              uuid,
+              dev_checkpoint->settings_connection_is_unsaved,
+              sett_conn_unsaved);
+        *need_update = TRUE;
+    }
+
+    /* Check if the active state changed */
     nm_manager_for_each_active_connection (priv->manager, active, tmp_clist) {
         ac_uuid =
             nm_settings_connection_get_uuid(nm_active_connection_get_settings_connection(active));
@@ -193,7 +275,7 @@ find_settings_connection(NMCheckpoint     *self,
         return sett_conn;
     }
 
-    /* ... or if the connection was reactivated/reapplied */
+    /* Check if the connection was reactivated/reapplied */
     if (nm_active_connection_version_id_get(active) != dev_checkpoint->ac_version_id) {
         _LOGT("rollback: active connection version id of %s changed", uuid);
         *need_activation = TRUE;
@@ -209,12 +291,19 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
     NMSettingsConnection           *connection;
     gs_unref_object NMAuthSubject  *subject     = NULL;
     GError                         *local_error = NULL;
-    gboolean                        need_update, need_activation;
+    gboolean                        need_update;
+    gboolean                        need_update_shadowed;
+    gboolean                        need_activation;
     NMSettingsConnectionPersistMode persist_mode;
     NMSettingsConnectionIntFlags    sett_flags;
     NMSettingsConnectionIntFlags    sett_mask;
 
-    connection = find_settings_connection(self, dev_checkpoint, &need_update, &need_activation);
+    connection = find_settings_connection(self,
+                                          dev_checkpoint,
+                                          &need_update,
+                                          &need_update_shadowed,
+                                          &need_activation,
+                                          &persist_mode);
 
     /* FIXME: we need to ensure to re-create/update the profile for the
      *   same settings plugin. E.g. if it was a keyfile in /run or /etc,
@@ -226,9 +315,26 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
     sett_mask  = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE;
 
     if (connection) {
+        if (need_update_shadowed) {
+            _LOGD("rollback: updating shadowed file for connection %s",
+                  nm_connection_get_uuid(dev_checkpoint->settings_connection));
+            nm_settings_connection_update(
+                connection,
+                NULL,
+                dev_checkpoint->settings_connection_shadowed,
+                NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK,
+                sett_flags,
+                sett_mask,
+                NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+                    | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET,
+                "checkpoint-rollback",
+                NULL);
+        }
+
         if (need_update) {
-            _LOGD("rollback: updating connection %s", nm_settings_connection_get_uuid(connection));
-            persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
+            _LOGD("rollback: updating connection %s with persist mode \"%s\"",
+                  nm_connection_get_uuid(dev_checkpoint->settings_connection),
+                  nm_settings_connection_persist_mode_to_string(persist_mode));
             nm_settings_connection_update(
                 connection,
                 NULL,
@@ -243,21 +349,54 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
         }
     } else {
         /* The connection was deleted, recreate it */
-        _LOGD("rollback: adding connection %s again",
-              nm_connection_get_uuid(dev_checkpoint->settings_connection));
-
-        persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
-        if (!nm_settings_add_connection(NM_SETTINGS_GET,
-                                        NULL,
-                                        dev_checkpoint->settings_connection,
-                                        persist_mode,
-                                        NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
-                                        sett_flags,
-                                        &connection,
-                                        &local_error)) {
-            _LOGD("rollback: connection add failure: %s", local_error->message);
-            g_clear_error(&local_error);
-            return FALSE;
+        if (need_update_shadowed) {
+            _LOGD("rollback: adding back shadowed file for connection %s",
+                  nm_connection_get_uuid(dev_checkpoint->settings_connection));
+
+            if (!nm_settings_add_connection(NM_SETTINGS_GET,
+                                            NULL,
+                                            dev_checkpoint->settings_connection_shadowed,
+                                            NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK,
+                                            NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
+                                            sett_flags,
+                                            &connection,
+                                            &local_error)) {
+                _LOGD("rollback: connection add failure: %s", local_error->message);
+                g_clear_error(&local_error);
+                return FALSE;
+            }
+
+            _LOGD("rollback: updating connection %s with persist mode \"%s\"",
+                  nm_connection_get_uuid(dev_checkpoint->settings_connection),
+                  nm_settings_connection_persist_mode_to_string(persist_mode));
+
+            nm_settings_connection_update(
+                connection,
+                NULL,
+                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 {
+            _LOGD("rollback: adding back connection %s with persist mode \"%s\"",
+                  nm_connection_get_uuid(dev_checkpoint->settings_connection),
+                  nm_settings_connection_persist_mode_to_string(persist_mode));
+            if (!nm_settings_add_connection(NM_SETTINGS_GET,
+                                            NULL,
+                                            dev_checkpoint->settings_connection,
+                                            persist_mode,
+                                            NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
+                                            sett_flags,
+                                            &connection,
+                                            &local_error)) {
+                _LOGD("rollback: connection add failure: %s", local_error->message);
+                g_clear_error(&local_error);
+                return FALSE;
+            }
         }
         need_activation = TRUE;
     }
@@ -359,11 +498,15 @@ nm_checkpoint_rollback(NMCheckpoint *self)
     while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
         guint32 result = NM_ROLLBACK_RESULT_OK;
 
-        _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)",
+        _LOGD("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d, "
+              "connection-unsaved %d, connection-shadowed %d, connection-shadowed-owned %d)",
               dev_checkpoint->original_dev_name,
               (int) dev_checkpoint->state,
               dev_checkpoint->realized,
-              dev_checkpoint->unmanaged_explicit);
+              dev_checkpoint->unmanaged_explicit,
+              dev_checkpoint->settings_connection_is_unsaved,
+              !!dev_checkpoint->settings_connection_shadowed,
+              dev_checkpoint->settings_connection_is_shadowed_owned);
 
         if (nm_device_is_real(device)) {
             if (!dev_checkpoint->realized) {
@@ -491,6 +634,17 @@ next_dev:
             }
         }
     }
+    if (NM_FLAGS_HAS(priv->flags, NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS)
+        && priv->global_dns_config) {
+        gs_free_error GError *error = NULL;
+        NMConfig             *config;
+
+        config = nm_manager_get_config(priv->manager);
+        nm_assert(config);
+        if (!nm_config_set_global_dns(config, priv->global_dns_config, &error)) {
+            _LOGE("set global DNS failed with error: %s", error->message);
+        }
+    }
 
     return g_variant_new("(a{su})", &builder);
 }
@@ -504,6 +658,7 @@ device_checkpoint_destroy(gpointer data)
     g_clear_object(&dev_checkpoint->applied_connection);
     g_clear_object(&dev_checkpoint->settings_connection);
     g_clear_object(&dev_checkpoint->device);
+    g_clear_object(&dev_checkpoint->settings_connection_shadowed);
     g_free(dev_checkpoint->original_dev_path);
     g_free(dev_checkpoint->original_dev_name);
 
@@ -541,7 +696,7 @@ _dev_exported_changed(NMDBusObject *obj, NMCheckpoint *checkpoint)
 }
 
 static DeviceCheckpoint *
-device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device)
+device_checkpoint_create(NMCheckpoint *self, NMDevice *device)
 {
     DeviceCheckpoint     *dev_checkpoint;
     NMConnection         *applied_connection;
@@ -565,7 +720,7 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device)
     dev_checkpoint->dev_exported_change_id = g_signal_connect(device,
                                                               NM_DBUS_OBJECT_EXPORTED_CHANGED,
                                                               G_CALLBACK(_dev_exported_changed),
-                                                              checkpoint);
+                                                              self);
 
     if (nm_device_get_unmanaged_mask(device, NM_UNMANAGED_USER_EXPLICIT)) {
         dev_checkpoint->unmanaged_explicit =
@@ -575,6 +730,11 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device)
 
     act_request = nm_device_get_act_request(device);
     if (act_request) {
+        NMSettingsStorage    *storage;
+        gboolean              shadowed_owned = FALSE;
+        const char           *shadowed_file;
+        gs_free_error GError *error = NULL;
+
         settings_connection = nm_act_request_get_settings_connection(act_request);
         applied_connection  = nm_act_request_get_applied_connection(act_request);
 
@@ -588,6 +748,24 @@ device_checkpoint_create(NMCheckpoint *checkpoint, NMDevice *device)
         dev_checkpoint->activation_lifetime_bound_to_profile_visibility =
             NM_FLAGS_HAS(nm_active_connection_get_state_flags(NM_ACTIVE_CONNECTION(act_request)),
                          NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY);
+
+        dev_checkpoint->settings_connection_is_unsaved =
+            NM_FLAGS_HAS(nm_settings_connection_get_flags(settings_connection),
+                         NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED);
+
+        storage = nm_settings_connection_get_storage(settings_connection);
+        shadowed_file =
+            storage ? nm_settings_storage_get_shadowed_storage(storage, &shadowed_owned) : NULL;
+        if (shadowed_file) {
+            dev_checkpoint->settings_connection_is_shadowed_owned = shadowed_owned;
+            dev_checkpoint->settings_connection_shadowed =
+                parse_connection_from_shadowed_file(shadowed_file, &error);
+            if (!dev_checkpoint->settings_connection_shadowed) {
+                _LOGW("error reading shadowed connection file for %s: %s",
+                      nm_device_get_iface(device),
+                      error->message);
+            }
+        }
     }
 
     return dev_checkpoint;
@@ -742,6 +920,19 @@ nm_checkpoint_new(NMManager              *manager,
                                             NM_MANAGER_DEVICE_REMOVED,
                                             G_CALLBACK(_device_removed),
                                             self);
+    if (NM_FLAGS_HAS(flags, NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS)) {
+        NMConfigData      *config_data;
+        NMGlobalDnsConfig *dns_config = NULL;
+
+        config_data = nm_config_get_data(nm_manager_get_config(manager));
+        if (config_data) {
+            dns_config = nm_config_data_get_global_dns_config(config_data);
+            if (!dns_config || nm_global_dns_config_is_internal(dns_config)) {
+                priv->global_dns_config = nm_global_dns_config_clone(dns_config);
+            }
+        }
+    }
+
     return self;
 }
 
@@ -756,6 +947,7 @@ dispose(GObject *object)
     nm_clear_pointer(&priv->devices, g_hash_table_unref);
     nm_clear_pointer(&priv->connection_uuids, g_hash_table_unref);
     nm_clear_pointer(&priv->removed_devices, g_ptr_array_unref);
+    nm_global_dns_config_free(priv->global_dns_config);
 
     nm_clear_g_signal_handler(priv->manager, &priv->dev_removed_id);
     g_clear_object(&priv->manager);
diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c
index 468e56b8..d4498edd 100644
--- a/src/core/nm-config-data.c
+++ b/src/core/nm-config-data.c
@@ -2436,3 +2436,50 @@ nm_config_data_class_init(NMConfigDataClass *config_class)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
+
+static NMGlobalDnsDomain *
+nm_global_dns_domain_clone(NMGlobalDnsDomain *old_domain)
+{
+    if (old_domain) {
+        NMGlobalDnsDomain *new_domain = g_malloc0(sizeof(NMGlobalDnsDomain));
+        new_domain->name              = g_strdup(old_domain->name);
+        new_domain->servers           = (char **) nm_strv_dup(old_domain->servers, -1, TRUE);
+        new_domain->options           = (char **) nm_strv_dup(old_domain->options, -1, TRUE);
+        return new_domain;
+    } else {
+        return NULL;
+    }
+}
+
+NMGlobalDnsConfig *
+nm_global_dns_config_clone(NMGlobalDnsConfig *old_dns_config)
+{
+    NMGlobalDnsConfig *new_dns_config;
+    gpointer           key, value;
+    NMGlobalDnsDomain *old_domain;
+    GHashTableIter     iter;
+
+    new_dns_config           = g_malloc0(sizeof(NMGlobalDnsConfig));
+    new_dns_config->internal = TRUE;
+
+    if (old_dns_config) {
+        new_dns_config->internal = old_dns_config->internal;
+        new_dns_config->searches = nm_strv_dup(old_dns_config->searches, -1, TRUE);
+        new_dns_config->options  = nm_strv_dup(old_dns_config->options, -1, TRUE);
+        new_dns_config->domains  = g_hash_table_new_full(nm_str_hash,
+                                                        g_str_equal,
+                                                        g_free,
+                                                        (GDestroyNotify) global_dns_domain_free);
+        if (old_dns_config->domains) {
+            g_hash_table_iter_init(&iter, old_dns_config->domains);
+            while (g_hash_table_iter_next(&iter, &key, &value)) {
+                old_domain = value;
+                g_hash_table_insert(new_dns_config->domains,
+                                    g_strdup(key),
+                                    nm_global_dns_domain_clone(old_domain));
+            }
+        }
+        global_dns_config_seal_domains(new_dns_config);
+    }
+    return new_dns_config;
+}
diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h
index 9e7a50fc..0344ce90 100644
--- a/src/core/nm-config-data.h
+++ b/src/core/nm-config-data.h
@@ -280,7 +280,8 @@ int                nm_global_dns_config_cmp(const NMGlobalDnsConfig *a,
                                             const NMGlobalDnsConfig *b,
                                             gboolean                 check_internal);
 void nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum);
-void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config);
+NMGlobalDnsConfig *nm_global_dns_config_clone(NMGlobalDnsConfig *dns_config);
+void               nm_global_dns_config_free(NMGlobalDnsConfig *dns_config);
 
 NMGlobalDnsConfig *nm_global_dns_config_from_dbus(const GValue *value, GError **error);
 void               nm_global_dns_config_to_dbus(const NMGlobalDnsConfig *dns_config, GValue *value);
diff --git a/src/core/nm-connectivity.h b/src/core/nm-connectivity.h
index d9ea95c2..e0353f9f 100644
--- a/src/core/nm-connectivity.h
+++ b/src/core/nm-connectivity.h
@@ -25,10 +25,10 @@ nm_connectivity_state_cmp(NMConnectivityState a, NMConnectivityState b)
 
 /*****************************************************************************/
 
-#define NM_CONNECTIVITY_ERROR     ((NMConnectivityState) -1)
-#define NM_CONNECTIVITY_FAKE      ((NMConnectivityState) -2)
-#define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) -3)
-#define NM_CONNECTIVITY_DISPOSING ((NMConnectivityState) -4)
+#define NM_CONNECTIVITY_ERROR     ((NMConnectivityState) - 1)
+#define NM_CONNECTIVITY_FAKE      ((NMConnectivityState) - 2)
+#define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) - 3)
+#define NM_CONNECTIVITY_DISPOSING ((NMConnectivityState) - 4)
 
 #define NM_TYPE_CONNECTIVITY (nm_connectivity_get_type())
 #define NM_CONNECTIVITY(obj) \
diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c
index 178ea3c4..dd1e9939 100644
--- a/src/core/nm-core-utils.c
+++ b/src/core/nm-core-utils.c
@@ -121,7 +121,8 @@ _nm_singleton_instance_weak_cb(gpointer data, GObject *where_the_object_was)
     _singletons = g_slist_remove(_singletons, where_the_object_was);
 }
 
-static void __attribute__((destructor)) _nm_singleton_instance_destroy(void)
+static void __attribute__((destructor))
+_nm_singleton_instance_destroy(void)
 {
     _singletons_shutdown = TRUE;
 
diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h
index f015236d..fdfed5f6 100644
--- a/src/core/nm-core-utils.h
+++ b/src/core/nm-core-utils.h
@@ -306,7 +306,7 @@ typedef enum {
     NM_UTILS_STABLE_TYPE_RANDOM    = 3,
 } NMUtilsStableType;
 
-#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) -1)
+#define NM_UTILS_STABLE_TYPE_NONE ((NMUtilsStableType) - 1)
 
 NMUtilsStableType nm_utils_stable_id_parse(const char *stable_id,
                                            const char *deviceid,
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index f428d04c..12357fba 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -4338,7 +4338,7 @@ _l3_commit_ndisc_params(NML3Cfg *self, NML3CfgCommitType commit_type)
     if (l3cd) {
         reachable_set = nm_l3_config_data_get_ndisc_reachable_time_msec(l3cd, &reachable);
         retrans_set   = nm_l3_config_data_get_ndisc_retrans_timer_msec(l3cd, &retrans);
-        hop_limit     = nm_l3_config_data_get_ndisc_hop_limit(l3cd, &hop_limit);
+        hop_limit_set = nm_l3_config_data_get_ndisc_hop_limit(l3cd, &hop_limit);
     }
     ifname = nm_l3cfg_get_ifname(self, TRUE);
 
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index 730ba476..b2a827e3 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -46,7 +46,7 @@
 #include "nm-priv-helper-call.h"
 #include "nm-rfkill-manager.h"
 #include "nm-session-monitor.h"
-#include "nm-sleep-monitor.h"
+#include "nm-power-monitor.h"
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
 #include "vpn/nm-vpn-manager.h"
@@ -214,7 +214,7 @@ typedef struct {
 
     NMVpnManager *vpn_manager;
 
-    NMSleepMonitor *sleep_monitor;
+    NMPowerMonitor *power_monitor;
 
     NMAuthManager *auth_mgr;
 
@@ -1914,7 +1914,8 @@ find_device_by_ip_iface(NMManager *self, const char *iface)
  * is given, this function will only return master devices and will ensure
  * @slave, when activated, can be a slave of the returned master device.  If
  * @connection is given, this function will only consider devices that are
- * compatible with @connection.
+ * compatible with @connection. If @child is given, this function will only
+ * return parent device.
  *
  * Returns: the matching #NMDevice
  */
@@ -1922,7 +1923,8 @@ static NMDevice *
 find_device_by_iface(NMManager    *self,
                      const char   *iface,
                      NMConnection *connection,
-                     NMConnection *slave)
+                     NMConnection *slave,
+                     NMConnection *child)
 {
     NMManagerPrivate *priv     = NM_MANAGER_GET_PRIVATE(self);
     NMDevice         *fallback = NULL;
@@ -1936,11 +1938,13 @@ find_device_by_iface(NMManager    *self,
         if (connection && !nm_device_check_connection_compatible(candidate, connection, TRUE, NULL))
             continue;
         if (slave) {
-            if (!nm_device_is_master(candidate))
+            if (!nm_device_is_controller(candidate))
                 continue;
             if (!nm_device_check_slave_connection_compatible(candidate, slave))
                 continue;
         }
+        if (child && !nm_device_can_be_parent(candidate))
+            continue;
 
         if (nm_device_is_real(candidate))
             return candidate;
@@ -2405,7 +2409,7 @@ find_parent_device_for_connection(NMManager       *self,
     NM_SET_OUT(out_parent_spec, parent_name);
 
     /* Try as an interface name of a parent device */
-    parent = find_device_by_iface(self, parent_name, NULL, NULL);
+    parent = find_device_by_iface(self, parent_name, NULL, NULL, connection);
     if (parent)
         return parent;
 
@@ -3777,7 +3781,7 @@ recheck_assume_connection(NMManager *self, NMDevice *device)
                         &master_ac,
                         NULL)
             && master_ac)
-            nm_active_connection_set_master(active, master_ac);
+            nm_active_connection_set_controller(active, master_ac);
 
         active_connection_add(self, active);
         nm_device_queue_activation(device, NM_ACT_REQUEST(active));
@@ -4434,10 +4438,25 @@ platform_query_devices(NMManager *self)
     links        = nm_platform_link_get_all(priv->platform);
     if (!links)
         return;
+
     for (i = 0; i < links->len; i++) {
-        const NMPlatformLink          *link = NMP_OBJECT_CAST_LINK(links->pdata[i]);
+        const NMPlatformLink          *elem = NMP_OBJECT_CAST_LINK(links->pdata[i]);
+        const NMPlatformLink          *link;
         const NMConfigDeviceStateData *dev_state;
 
+        /*
+         * @links is an immutable snapshot of the platform links captured before
+         * the loop was started. It's possible that in the meantime, while
+         * processing netlink events in platform_link_added(), a link was
+         * renamed.  If that happens, we have 2 different views of the same
+         * ifindex: the one from @links and the one from platform. This can
+         * cause race conditions; make sure to use the latest known version of
+         * the link.
+         */
+        link = nm_platform_link_get(priv->platform, elem->ifindex);
+        if (!link)
+            continue;
+
         dev_state = nm_config_device_state_get(priv->config, link->ifindex);
         platform_link_added(self,
                             link->ifindex,
@@ -4921,7 +4940,7 @@ find_master(NMManager             *self,
     nm_assert(!out_master_ac || !*out_master_ac);
 
     s_con  = nm_connection_get_setting_connection(connection);
-    master = nm_setting_connection_get_master(s_con);
+    master = nm_setting_connection_get_controller(s_con);
 
     if (master == NULL)
         return TRUE; /* success, but no master */
@@ -5003,7 +5022,7 @@ find_master(NMManager             *self,
     }
 
     if (!master_connection) {
-        master_device = find_device_by_iface(self, master, NULL, connection);
+        master_device = find_device_by_iface(self, master, NULL, connection, NULL);
         if (!master_device) {
             g_set_error(error,
                         NM_MANAGER_ERROR,
@@ -5924,7 +5943,7 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError **
                                              NM_DEVICE_STATE_REASON_USER_REQUESTED);
         }
 
-        nm_active_connection_set_master(active, master_ac);
+        nm_active_connection_set_controller(active, master_ac);
         _LOGD(LOGD_CORE,
               "Activation of '%s' depends on active connection %p %s",
               nm_settings_connection_get_id(sett_conn),
@@ -6445,7 +6464,7 @@ validate_activation_request(NMManager             *self,
             if (!iface)
                 return NULL;
 
-            device = find_device_by_iface(self, iface, connection, NULL);
+            device = find_device_by_iface(self, iface, connection, NULL, NULL);
             if (!device) {
                 g_set_error_literal(error,
                                     NM_MANAGER_ERROR,
@@ -7128,7 +7147,7 @@ static gboolean
 sleep_devices_add(NMManager *self, NMDevice *device, gboolean suspending)
 {
     NMManagerPrivate              *priv   = NM_MANAGER_GET_PRIVATE(self);
-    NMSleepMonitorInhibitorHandle *handle = NULL;
+    NMPowerMonitorInhibitorHandle *handle = NULL;
 
     if (g_hash_table_lookup_extended(priv->sleep_devices, device, NULL, (gpointer *) &handle)) {
         if (suspending) {
@@ -7136,16 +7155,16 @@ sleep_devices_add(NMManager *self, NMDevice *device, gboolean suspending)
              * Even if we had an old handle, it might be stale by now. */
             g_hash_table_insert(priv->sleep_devices,
                                 device,
-                                nm_sleep_monitor_inhibit_take(priv->sleep_monitor));
+                                nm_power_monitor_inhibit_take(priv->power_monitor));
             if (handle)
-                nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle);
+                nm_power_monitor_inhibit_release(priv->power_monitor, handle);
         }
         return FALSE;
     }
 
     g_hash_table_insert(priv->sleep_devices,
                         g_object_ref(device),
-                        suspending ? nm_sleep_monitor_inhibit_take(priv->sleep_monitor) : NULL);
+                        suspending ? nm_power_monitor_inhibit_take(priv->power_monitor) : NULL);
     g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(device_sleep_cb), self);
     return TRUE;
 }
@@ -7154,13 +7173,13 @@ static gboolean
 sleep_devices_remove(NMManager *self, NMDevice *device)
 {
     NMManagerPrivate              *priv = NM_MANAGER_GET_PRIVATE(self);
-    NMSleepMonitorInhibitorHandle *handle;
+    NMPowerMonitorInhibitorHandle *handle;
 
     if (!g_hash_table_lookup_extended(priv->sleep_devices, device, NULL, (gpointer *) &handle))
         return FALSE;
 
     if (handle)
-        nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle);
+        nm_power_monitor_inhibit_release(priv->power_monitor, handle);
 
     /* Remove device from hash */
     g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self);
@@ -7177,14 +7196,14 @@ sleep_devices_clear(NMManager *self)
 {
     NMManagerPrivate              *priv = NM_MANAGER_GET_PRIVATE(self);
     NMDevice                      *device;
-    NMSleepMonitorInhibitorHandle *handle;
+    NMPowerMonitorInhibitorHandle *handle;
     GHashTableIter                 iter;
 
     g_hash_table_iter_init(&iter, priv->sleep_devices);
     while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &handle)) {
         g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self);
         if (handle)
-            nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle);
+            nm_power_monitor_inhibit_release(priv->power_monitor, handle);
         g_object_unref(device);
         g_hash_table_iter_remove(&iter);
     }
@@ -7216,6 +7235,33 @@ device_sleep_cb(NMDevice *device, GParamSpec *pspec, NMManager *self)
 }
 
 static void
+_handle_device_takedown(NMManager *self,
+                        NMDevice  *device,
+                        gboolean   suspending,
+                        gboolean   is_shutdown)
+{
+    nm_device_notify_sleeping(device);
+
+    if (nm_device_is_activating(device)
+        || nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) {
+        _LOGD(LOGD_SUSPEND,
+              "%s: wait disconnection of device %s",
+              is_shutdown ? "shutdown" : "sleep",
+              nm_device_get_ip_iface(device));
+
+        if (sleep_devices_add(self, device, suspending))
+            nm_device_queue_state(device,
+                                  NM_DEVICE_STATE_DEACTIVATING,
+                                  NM_DEVICE_STATE_REASON_SLEEPING);
+    } else {
+        nm_device_set_unmanaged_by_flags(device,
+                                         NM_UNMANAGED_SLEEPING,
+                                         NM_UNMAN_FLAG_OP_SET_UNMANAGED,
+                                         NM_DEVICE_STATE_REASON_SLEEPING);
+    }
+}
+
+static void
 do_sleep_wake(NMManager *self, gboolean sleeping_changed)
 {
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
@@ -7249,24 +7295,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
                 continue;
             }
 
-            nm_device_notify_sleeping(device);
-
-            if (nm_device_is_activating(device)
-                || nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) {
-                _LOGD(LOGD_SUSPEND,
-                      "sleep: wait disconnection of device %s",
-                      nm_device_get_ip_iface(device));
-
-                if (sleep_devices_add(self, device, suspending))
-                    nm_device_queue_state(device,
-                                          NM_DEVICE_STATE_DEACTIVATING,
-                                          NM_DEVICE_STATE_REASON_SLEEPING);
-            } else {
-                nm_device_set_unmanaged_by_flags(device,
-                                                 NM_UNMANAGED_SLEEPING,
-                                                 NM_UNMAN_FLAG_OP_SET_UNMANAGED,
-                                                 NM_DEVICE_STATE_REASON_SLEEPING);
-            }
+            _handle_device_takedown(self, device, suspending, FALSE);
         }
     } else {
         _LOGD(LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling");
@@ -7438,7 +7467,7 @@ impl_manager_sleep(NMDBusObject                      *obj,
 }
 
 static void
-sleeping_cb(NMSleepMonitor *monitor, gboolean is_about_to_suspend, gpointer user_data)
+sleeping_cb(NMPowerMonitor *monitor, gboolean is_about_to_suspend, gpointer user_data)
 {
     NMManager *self = user_data;
 
@@ -7447,6 +7476,41 @@ sleeping_cb(NMSleepMonitor *monitor, gboolean is_about_to_suspend, gpointer user
 }
 
 static void
+shutdown_cb(NMPowerMonitor *monitor, gpointer user_data)
+{
+    NMManager        *self = user_data;
+    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
+    NMDevice         *device;
+
+    _LOGT(LOGD_SUSPEND, "shutdown: received shutdown signal");
+
+    c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+        NMSettingConnection *s_con;
+        gboolean             take_down = FALSE;
+
+        s_con = nm_device_get_applied_setting(device, NM_TYPE_SETTING_CONNECTION);
+        if (!s_con)
+            continue;
+
+        if (nm_setting_connection_get_down_on_poweroff(s_con)
+            == NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES)
+            take_down = TRUE;
+        else if (nm_setting_connection_get_down_on_poweroff(s_con)
+                 == NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT)
+            take_down = nm_config_data_get_connection_default_int64(
+                NM_CONFIG_GET_DATA,
+                NM_CON_DEFAULT("connection.down-on-poweroff"),
+                device,
+                NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO,
+                NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES,
+                NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO);
+
+        if (take_down)
+            _handle_device_takedown(self, device, FALSE, TRUE);
+    }
+}
+
+static void
 _internal_enable(NMManager *self, gboolean enable)
 {
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
@@ -8458,7 +8522,8 @@ impl_manager_checkpoint_create(NMDBusObject                      *obj,
                                      | NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS
                                      | NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES
                                      | NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING
-                                     | NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS)))) {
+                                     | NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS
+                                     | NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS)))) {
         g_dbus_method_invocation_return_error_literal(invocation,
                                                       NM_MANAGER_ERROR,
                                                       NM_MANAGER_ERROR_INVALID_ARGUMENTS,
@@ -8834,8 +8899,9 @@ nm_manager_init(NMManager *self)
     priv->devcon_data_dict = g_hash_table_new(_devcon_data_hash, _devcon_data_equal);
 
     /* sleep/wake handling */
-    priv->sleep_monitor = nm_sleep_monitor_new();
-    g_signal_connect(priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING, G_CALLBACK(sleeping_cb), self);
+    priv->power_monitor = nm_power_monitor_new();
+    g_signal_connect(priv->power_monitor, NM_POWER_MONITOR_SLEEPING, G_CALLBACK(sleeping_cb), self);
+    g_signal_connect(priv->power_monitor, NM_POWER_MONITOR_SHUTDOWN, G_CALLBACK(shutdown_cb), self);
 
     /* Listen for authorization changes */
     priv->auth_mgr = g_object_ref(nm_auth_manager_get());
@@ -8979,9 +9045,10 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_CHECKPOINTS:
         g_value_take_boxed(
             value,
-            priv->checkpoint_mgr ? nm_strv_make_deep_copied(
-                nm_checkpoint_manager_get_checkpoint_paths(priv->checkpoint_mgr, NULL))
-                                 : NULL);
+            priv->checkpoint_mgr
+                ? nm_strv_make_deep_copied(
+                      nm_checkpoint_manager_get_checkpoint_paths(priv->checkpoint_mgr, NULL))
+                : NULL);
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -9134,9 +9201,9 @@ dispose(GObject *object)
         nm_clear_pointer(&priv->sleep_devices, g_hash_table_unref);
     }
 
-    if (priv->sleep_monitor) {
-        g_signal_handlers_disconnect_by_func(priv->sleep_monitor, sleeping_cb, self);
-        g_clear_object(&priv->sleep_monitor);
+    if (priv->power_monitor) {
+        g_signal_handlers_disconnect_by_func(priv->power_monitor, sleeping_cb, self);
+        g_clear_object(&priv->power_monitor);
     }
 
     if (priv->fw_monitor) {
@@ -9765,3 +9832,11 @@ nm_manager_class_init(NMManagerClass *manager_class)
                                                    1,
                                                    NM_TYPE_DEVICE);
 }
+
+NMConfig *
+nm_manager_get_config(NMManager *self)
+{
+    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
+
+    return priv->config;
+}
diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h
index 3028eb7e..3c5213c4 100644
--- a/src/core/nm-manager.h
+++ b/src/core/nm-manager.h
@@ -10,6 +10,7 @@
 #include "settings/nm-settings-connection.h"
 #include "c-list/src/c-list.h"
 #include "nm-dbus-manager.h"
+#include "nm-config-data.h"
 
 #define NM_TYPE_MANAGER            (nm_manager_get_type())
 #define NM_MANAGER(obj)            (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_MANAGER, NMManager))
@@ -266,4 +267,6 @@ gboolean nm_manager_devcon_autoconnect_blocked_reason_set(NMManager            *
                                                           NMSettingsAutoconnectBlockedReason value,
                                                           gboolean                           set);
 
+NMConfig *nm_manager_get_config(NMManager *self);
+
 #endif /* __NETWORKMANAGER_MANAGER_H__ */
diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c
index feea97b1..60d11c75 100644
--- a/src/core/nm-policy.c
+++ b/src/core/nm-policy.c
@@ -1805,7 +1805,7 @@ unblock_autoconnect_for_ports(NMPolicy   *self,
 
         s_slave_con =
             nm_settings_connection_get_setting(sett_conn, NM_META_SETTING_TYPE_CONNECTION);
-        slave_master = nm_setting_connection_get_master(s_slave_con);
+        slave_master = nm_setting_connection_get_controller(s_slave_con);
         if (!slave_master)
             continue;
 
diff --git a/src/core/nm-sleep-monitor.c b/src/core/nm-power-monitor.c
index 66ea2f6c..289b0ff0 100644
--- a/src/core/nm-sleep-monitor.c
+++ b/src/core/nm-power-monitor.c
@@ -6,7 +6,7 @@
 
 #include "src/core/nm-default-daemon.h"
 
-#include "nm-sleep-monitor.h"
+#include "nm-power-monitor.h"
 
 #include <sys/stat.h>
 #include <gio/gunixfdlist.h>
@@ -15,24 +15,15 @@
 #include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
-#if defined(SUSPEND_RESUME_UPOWER)
-
-#define SUSPEND_DBUS_NAME      "org.freedesktop.UPower"
-#define SUSPEND_DBUS_PATH      "/org/freedesktop/UPower"
-#define SUSPEND_DBUS_INTERFACE "org.freedesktop.UPower"
-#define USE_UPOWER             1
-#define _NMLOG_PREFIX_NAME     "sleep-monitor-up"
-
-#elif defined(SUSPEND_RESUME_SYSTEMD) || defined(SUSPEND_RESUME_ELOGIND)
+#if defined(SUSPEND_RESUME_SYSTEMD) || defined(SUSPEND_RESUME_ELOGIND)
 
 #define SUSPEND_DBUS_NAME      "org.freedesktop.login1"
 #define SUSPEND_DBUS_PATH      "/org/freedesktop/login1"
 #define SUSPEND_DBUS_INTERFACE "org.freedesktop.login1.Manager"
-#define USE_UPOWER             0
 #if defined(SUSPEND_RESUME_SYSTEMD)
-#define _NMLOG_PREFIX_NAME "sleep-monitor-sd"
+#define _NMLOG_PREFIX_NAME "power-monitor-sd"
 #else
-#define _NMLOG_PREFIX_NAME "sleep-monitor-el"
+#define _NMLOG_PREFIX_NAME "power-monitor-el"
 #endif
 
 #elif defined(SUSPEND_RESUME_CONSOLEKIT)
@@ -44,12 +35,11 @@
 #define SUSPEND_DBUS_NAME      "org.freedesktop.ConsoleKit"
 #define SUSPEND_DBUS_PATH      "/org/freedesktop/ConsoleKit/Manager"
 #define SUSPEND_DBUS_INTERFACE "org.freedesktop.ConsoleKit.Manager"
-#define USE_UPOWER             0
-#define _NMLOG_PREFIX_NAME     "sleep-monitor-ck"
+#define _NMLOG_PREFIX_NAME     "power-monitor-ck"
 
 #else
 
-#error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_ELOGIND, SUSPEND_RESUME_CONSOLEKIT, or SUSPEND_RESUME_UPOWER
+#error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_ELOGIND, SUSPEND_RESUME_CONSOLEKIT
 
 #endif
 
@@ -57,12 +47,13 @@
 
 enum {
     SLEEPING,
+    SHUTDOWN,
     LAST_SIGNAL,
 };
 
 static guint signals[LAST_SIGNAL] = {0};
 
-struct _NMSleepMonitor {
+struct _NMPowerMonitor {
     GObject parent;
 
     GDBusProxy *proxy;
@@ -76,13 +67,14 @@ struct _NMSleepMonitor {
 
     gulong sig_id_1;
     gulong sig_id_2;
+    gulong sig_id_3;
 };
 
-struct _NMSleepMonitorClass {
+struct _NMPowerMonitorClass {
     GObjectClass parent;
 };
 
-G_DEFINE_TYPE(NMSleepMonitor, nm_sleep_monitor, G_TYPE_OBJECT);
+G_DEFINE_TYPE(NMPowerMonitor, nm_power_monitor, G_TYPE_OBJECT);
 
 /*****************************************************************************/
 
@@ -91,28 +83,8 @@ G_DEFINE_TYPE(NMSleepMonitor, nm_sleep_monitor, G_TYPE_OBJECT);
 
 /*****************************************************************************/
 
-static void sleep_signal(NMSleepMonitor *self, gboolean is_about_to_suspend);
-
-/*****************************************************************************/
-
-#if USE_UPOWER
-
-static void
-upower_sleeping_cb(GDBusProxy *proxy, gpointer user_data)
-{
-    sleep_signal(user_data, TRUE);
-}
-
-static void
-upower_resuming_cb(GDBusProxy *proxy, gpointer user_data)
-{
-    sleep_signal(user_data, FALSE);
-}
-
-#else  /* USE_UPOWER */
-
 static void
-drop_inhibitor(NMSleepMonitor *self, gboolean force)
+drop_inhibitor(NMPowerMonitor *self, gboolean force)
 {
     if (!force && self->handles_active)
         return;
@@ -135,7 +107,7 @@ static void
 inhibit_done(GObject *source, GAsyncResult *result, gpointer user_data)
 {
     GDBusProxy                  *proxy   = G_DBUS_PROXY(source);
-    NMSleepMonitor              *self    = user_data;
+    NMPowerMonitor              *self    = user_data;
     gs_free_error GError        *error   = NULL;
     gs_unref_variant GVariant   *res     = NULL;
     gs_unref_object GUnixFDList *fd_list = NULL;
@@ -161,9 +133,9 @@ inhibit_done(GObject *source, GAsyncResult *result, gpointer user_data)
 }
 
 static void
-take_inhibitor(NMSleepMonitor *self)
+take_inhibitor(NMPowerMonitor *self)
 {
-    g_return_if_fail(NM_IS_SLEEP_MONITOR(self));
+    g_return_if_fail(NM_IS_POWER_MONITOR(self));
     g_return_if_fail(G_IS_DBUS_PROXY(self->proxy));
 
     drop_inhibitor(self, TRUE);
@@ -186,16 +158,10 @@ take_inhibitor(NMSleepMonitor *self)
 }
 
 static void
-prepare_for_sleep_cb(GDBusProxy *proxy, gboolean is_about_to_suspend, gpointer data)
-{
-    sleep_signal(data, is_about_to_suspend);
-}
-
-static void
 name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
 {
     GDBusProxy     *proxy = G_DBUS_PROXY(object);
-    NMSleepMonitor *self  = NM_SLEEP_MONITOR(user_data);
+    NMPowerMonitor *self  = NM_POWER_MONITOR(user_data);
     char           *owner;
 
     g_assert(proxy == self->proxy);
@@ -207,59 +173,64 @@ name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
         drop_inhibitor(self, TRUE);
     g_free(owner);
 }
-#endif /* USE_UPOWER */
 
 static void
-sleep_signal(NMSleepMonitor *self, gboolean is_about_to_suspend)
+prepare_for_sleep_cb(GDBusProxy *proxy, gboolean is_about_to_suspend, NMPowerMonitor *self)
 {
-    g_return_if_fail(NM_IS_SLEEP_MONITOR(self));
+    g_return_if_fail(NM_IS_POWER_MONITOR(self));
 
     _LOGD("received %s signal", is_about_to_suspend ? "SLEEP" : "RESUME");
 
-#if !USE_UPOWER
     if (!is_about_to_suspend)
         take_inhibitor(self);
-#endif
 
     g_signal_emit(self, signals[SLEEPING], 0, is_about_to_suspend);
 
-#if !USE_UPOWER
     if (is_about_to_suspend)
         drop_inhibitor(self, FALSE);
-#endif
+}
+
+static void
+prepare_for_shutdown_cb(GDBusProxy *proxy, gboolean is_about_to_shutdown, NMPowerMonitor *self)
+{
+    g_return_if_fail(NM_IS_POWER_MONITOR(self));
+
+    _LOGD("received SHUTDOWN signal");
+
+    g_signal_emit(self, signals[SHUTDOWN], 0);
 }
 
 /**
- * nm_sleep_monitor_inhibit_take:
- * @self: the #NMSleepMonitor instance
+ * nm_power_monitor_inhibit_take:
+ * @self: the #NMPowerMonitor instance
  *
  * Prevent the release of inhibitor lock
  *
  * Returns: an inhibitor handle that must be returned via
- *   nm_sleep_monitor_inhibit_release().
+ *   nm_power_monitor_inhibit_release().
  **/
-NMSleepMonitorInhibitorHandle *
-nm_sleep_monitor_inhibit_take(NMSleepMonitor *self)
+NMPowerMonitorInhibitorHandle *
+nm_power_monitor_inhibit_take(NMPowerMonitor *self)
 {
-    g_return_val_if_fail(NM_IS_SLEEP_MONITOR(self), NULL);
+    g_return_val_if_fail(NM_IS_POWER_MONITOR(self), NULL);
 
     self->handles_active = g_slist_prepend(self->handles_active, NULL);
-    return (NMSleepMonitorInhibitorHandle *) self->handles_active;
+    return (NMPowerMonitorInhibitorHandle *) self->handles_active;
 }
 
 /**
- * nm_sleep_monitor_inhibit_release:
- * @self: the #NMSleepMonitor instance
- * @handle: the #NMSleepMonitorInhibitorHandle inhibitor handle.
+ * nm_power_monitor_inhibit_release:
+ * @self: the #NMPowerMonitor instance
+ * @handle: the #NMPowerMonitorInhibitorHandle inhibitor handle.
  *
  * Allow again the release of inhibitor lock
  **/
 void
-nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHandle *handle)
+nm_power_monitor_inhibit_release(NMPowerMonitor *self, NMPowerMonitorInhibitorHandle *handle)
 {
     GSList *l;
 
-    g_return_if_fail(NM_IS_SLEEP_MONITOR(self));
+    g_return_if_fail(NM_IS_POWER_MONITOR(self));
     g_return_if_fail(handle);
 
     l = (GSList *) handle;
@@ -273,13 +244,11 @@ nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHa
 
     self->handles_active = g_slist_delete_link(self->handles_active, l);
 
-#if !USE_UPOWER
     drop_inhibitor(self, FALSE);
-#endif
 }
 
 static void
-on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self)
+on_proxy_acquired(GObject *object, GAsyncResult *res, NMPowerMonitor *self)
 {
     GError     *error = NULL;
     GDBusProxy *proxy;
@@ -294,18 +263,6 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self)
     self->proxy = proxy;
     g_clear_object(&self->cancellable);
 
-#if USE_UPOWER
-    self->sig_id_1 = _nm_dbus_proxy_signal_connect(self->proxy,
-                                                   "Sleeping",
-                                                   NULL,
-                                                   G_CALLBACK(upower_sleeping_cb),
-                                                   self);
-    self->sig_id_2 = _nm_dbus_proxy_signal_connect(self->proxy,
-                                                   "Resuming",
-                                                   NULL,
-                                                   G_CALLBACK(upower_resuming_cb),
-                                                   self);
-#else
     self->sig_id_1 =
         g_signal_connect(self->proxy, "notify::g-name-owner", G_CALLBACK(name_owner_cb), self);
     self->sig_id_2 = _nm_dbus_proxy_signal_connect(self->proxy,
@@ -313,6 +270,12 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self)
                                                    G_VARIANT_TYPE("(b)"),
                                                    G_CALLBACK(prepare_for_sleep_cb),
                                                    self);
+    self->sig_id_3 = _nm_dbus_proxy_signal_connect(self->proxy,
+                                                   "PrepareForShutdown",
+                                                   G_VARIANT_TYPE("(b)"),
+                                                   G_CALLBACK(prepare_for_shutdown_cb),
+                                                   self);
+
     {
         gs_free char *owner = NULL;
 
@@ -320,13 +283,12 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMSleepMonitor *self)
         if (owner)
             take_inhibitor(self);
     }
-#endif
 }
 
 /*****************************************************************************/
 
 static void
-nm_sleep_monitor_init(NMSleepMonitor *self)
+nm_power_monitor_init(NMPowerMonitor *self)
 {
     self->inhibit_fd  = -1;
     self->cancellable = g_cancellable_new();
@@ -342,34 +304,33 @@ nm_sleep_monitor_init(NMSleepMonitor *self)
                              self);
 }
 
-NMSleepMonitor *
-nm_sleep_monitor_new(void)
+NMPowerMonitor *
+nm_power_monitor_new(void)
 {
-    return g_object_new(NM_TYPE_SLEEP_MONITOR, NULL);
+    return g_object_new(NM_TYPE_POWER_MONITOR, NULL);
 }
 
 static void
 dispose(GObject *object)
 {
-    NMSleepMonitor *self = NM_SLEEP_MONITOR(object);
+    NMPowerMonitor *self = NM_POWER_MONITOR(object);
 
-#if !USE_UPOWER
     drop_inhibitor(self, TRUE);
-#endif
 
     nm_clear_g_cancellable(&self->cancellable);
 
     if (self->proxy) {
         nm_clear_g_signal_handler(self->proxy, &self->sig_id_1);
         nm_clear_g_signal_handler(self->proxy, &self->sig_id_2);
+        nm_clear_g_signal_handler(self->proxy, &self->sig_id_3);
         g_clear_object(&self->proxy);
     }
 
-    G_OBJECT_CLASS(nm_sleep_monitor_parent_class)->dispose(object);
+    G_OBJECT_CLASS(nm_power_monitor_parent_class)->dispose(object);
 }
 
 static void
-nm_sleep_monitor_class_init(NMSleepMonitorClass *klass)
+nm_power_monitor_class_init(NMPowerMonitorClass *klass)
 {
     GObjectClass *gobject_class;
 
@@ -377,8 +338,8 @@ nm_sleep_monitor_class_init(NMSleepMonitorClass *klass)
 
     gobject_class->dispose = dispose;
 
-    signals[SLEEPING] = g_signal_new(NM_SLEEP_MONITOR_SLEEPING,
-                                     NM_TYPE_SLEEP_MONITOR,
+    signals[SLEEPING] = g_signal_new(NM_POWER_MONITOR_SLEEPING,
+                                     NM_TYPE_POWER_MONITOR,
                                      G_SIGNAL_RUN_LAST,
                                      0,
                                      NULL,
@@ -387,4 +348,13 @@ nm_sleep_monitor_class_init(NMSleepMonitorClass *klass)
                                      G_TYPE_NONE,
                                      1,
                                      G_TYPE_BOOLEAN);
+    signals[SHUTDOWN] = g_signal_new(NM_POWER_MONITOR_SHUTDOWN,
+                                     NM_TYPE_POWER_MONITOR,
+                                     G_SIGNAL_RUN_LAST,
+                                     0,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     G_TYPE_NONE,
+                                     0);
 }
diff --git a/src/core/nm-power-monitor.h b/src/core/nm-power-monitor.h
new file mode 100644
index 00000000..269bb98a
--- /dev/null
+++ b/src/core/nm-power-monitor.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2012 - 2016 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#ifndef __NETWORKMANAGER_POWER_MONITOR_H__
+#define __NETWORKMANAGER_POWER_MONITOR_H__
+
+#define NM_TYPE_POWER_MONITOR (nm_power_monitor_get_type())
+#define NM_POWER_MONITOR(o) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_POWER_MONITOR, NMPowerMonitor))
+#define NM_POWER_MONITOR_CLASS(k) \
+    (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_POWER_MONITOR, NMPowerMonitorClass))
+#define NM_POWER_MONITOR_GET_CLASS(o) \
+    (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_POWER_MONITOR, NMPowerMonitorClass))
+#define NM_IS_POWER_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_POWER_MONITOR))
+#define NM_IS_POWER_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), NM_TYPE_POWER_MONITOR))
+
+#define NM_POWER_MONITOR_SLEEPING "sleeping"
+#define NM_POWER_MONITOR_SHUTDOWN "shutdown"
+
+typedef struct _NMPowerMonitorClass NMPowerMonitorClass;
+
+GType nm_power_monitor_get_type(void) G_GNUC_CONST;
+
+NMPowerMonitor *nm_power_monitor_new(void);
+
+typedef struct _NMPowerMonitorInhibitorHandle NMPowerMonitorInhibitorHandle;
+
+NMPowerMonitorInhibitorHandle *nm_power_monitor_inhibit_take(NMPowerMonitor *self);
+void nm_power_monitor_inhibit_release(NMPowerMonitor *self, NMPowerMonitorInhibitorHandle *handle);
+
+#endif /* __NETWORKMANAGER_POWER_MONITOR_H__ */
diff --git a/src/core/nm-sleep-monitor.h b/src/core/nm-sleep-monitor.h
deleted file mode 100644
index 0b7708db..00000000
--- a/src/core/nm-sleep-monitor.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2012 - 2016 Red Hat, Inc.
- * Author: Matthias Clasen <mclasen@redhat.com>
- */
-
-#ifndef __NETWORKMANAGER_SLEEP_MONITOR_H__
-#define __NETWORKMANAGER_SLEEP_MONITOR_H__
-
-#define NM_TYPE_SLEEP_MONITOR (nm_sleep_monitor_get_type())
-#define NM_SLEEP_MONITOR(o) \
-    (_NM_G_TYPE_CHECK_INSTANCE_CAST((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitor))
-#define NM_SLEEP_MONITOR_CLASS(k) \
-    (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_SLEEP_MONITOR, NMSleepMonitorClass))
-#define NM_SLEEP_MONITOR_GET_CLASS(o) \
-    (G_TYPE_INSTANCE_GET_CLASS((o), NM_TYPE_SLEEP_MONITOR, NMSleepMonitorClass))
-#define NM_IS_SLEEP_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE((o), NM_TYPE_SLEEP_MONITOR))
-#define NM_IS_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), NM_TYPE_SLEEP_MONITOR))
-
-#define NM_SLEEP_MONITOR_SLEEPING "sleeping"
-
-typedef struct _NMSleepMonitorClass NMSleepMonitorClass;
-
-GType nm_sleep_monitor_get_type(void) G_GNUC_CONST;
-
-NMSleepMonitor *nm_sleep_monitor_new(void);
-
-typedef struct _NMSleepMonitorInhibitorHandle NMSleepMonitorInhibitorHandle;
-
-NMSleepMonitorInhibitorHandle *nm_sleep_monitor_inhibit_take(NMSleepMonitor *self);
-void nm_sleep_monitor_inhibit_release(NMSleepMonitor *self, NMSleepMonitorInhibitorHandle *handle);
-
-#endif /* __NETWORKMANAGER_SLEEP_MONITOR_H__ */
diff --git a/src/core/nm-types.h b/src/core/nm-types.h
index 104a2f4b..6dfdc8e2 100644
--- a/src/core/nm-types.h
+++ b/src/core/nm-types.h
@@ -33,7 +33,7 @@ typedef struct _NMRfkillManager         NMRfkillManager;
 typedef struct _NMPacrunnerManager      NMPacrunnerManager;
 typedef struct _NMSessionMonitor        NMSessionMonitor;
 typedef struct _NMKeepAlive             NMKeepAlive;
-typedef struct _NMSleepMonitor          NMSleepMonitor;
+typedef struct _NMPowerMonitor          NMPowerMonitor;
 typedef struct _NMLldpListener          NMLldpListener;
 typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData;
 
@@ -96,6 +96,6 @@ typedef struct _NMSecretAgent        NMSecretAgent;
 typedef struct _NMSettings           NMSettings;
 typedef struct _NMSettingsConnection NMSettingsConnection;
 
-#define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) -42)
+#define NM_SETTING_CONNECTION_MDNS_UNKNOWN ((NMSettingConnectionMdns) - 42)
 
 #endif /* NM_TYPES_H */
diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c
index 176cc2c2..459c60ad 100644
--- a/src/core/settings/nm-settings-connection.c
+++ b/src/core/settings/nm-settings-connection.c
@@ -226,6 +226,29 @@ static guint _get_seen_bssids(NMSettingsConnection *self,
 
 /*****************************************************************************/
 
+char *
+nm_settings_connection_persist_mode_to_string(NMSettingsConnectionPersistMode mode)
+{
+    switch (mode) {
+    case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY:
+        return "in-memory";
+    case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED:
+        return "in-memory-detached";
+    case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY:
+        return "in-memory-only";
+    case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP:
+        return "keep";
+    case NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST:
+        return "no-persist";
+    case NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK:
+        return "to-disk";
+    }
+
+    return nm_assert_unreachable_val(NULL);
+}
+
+/*****************************************************************************/
+
 NMSettings *
 nm_settings_connection_get_settings(NMSettingsConnection *self)
 {
diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h
index 835a978e..d15a75b7 100644
--- a/src/core/settings/nm-settings-connection.h
+++ b/src/core/settings/nm-settings-connection.h
@@ -379,4 +379,8 @@ void _nm_settings_connection_emit_signal_updated_internal(
 
 void _nm_settings_connection_cleanup_after_remove(NMSettingsConnection *self);
 
+/*****************************************************************************/
+
+char *nm_settings_connection_persist_mode_to_string(NMSettingsConnectionPersistMode mode);
+
 #endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_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 3bcbb71b..7c2b2026 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
@@ -297,7 +297,7 @@ check_if_bond_slave(shvarFile *ifcfg, NMSettingConnection *s_con)
         v = svGetValueStr(ifcfg, "MASTER", &value);
 
     if (v) {
-        master = nm_setting_connection_get_master(s_con);
+        master = nm_setting_connection_get_controller(s_con);
         if (master) {
             PARSE_WARNING("Already configured as slave of %s. Ignoring MASTER{_UUID}=\"%s\"",
                           master,
@@ -331,7 +331,7 @@ check_if_team_slave(shvarFile *ifcfg, NMSettingConnection *s_con)
     if (!v)
         return;
 
-    master = nm_setting_connection_get_master(s_con);
+    master = nm_setting_connection_get_controller(s_con);
     if (master) {
         PARSE_WARNING("Already configured as slave of %s. Ignoring TEAM_MASTER{_UUID}=\"%s\"",
                       master,
@@ -507,7 +507,7 @@ make_connection_setting(const char *file,
     if (v) {
         const char *old_value;
 
-        if ((old_value = nm_setting_connection_get_master(s_con))) {
+        if ((old_value = nm_setting_connection_get_controller(s_con))) {
             PARSE_WARNING("Already configured as slave of %s. Ignoring BRIDGE=\"%s\"",
                           old_value,
                           v);
@@ -530,7 +530,7 @@ make_connection_setting(const char *file,
     if (v) {
         const char *old_value;
 
-        if ((old_value = nm_setting_connection_get_master(s_con))) {
+        if ((old_value = nm_setting_connection_get_controller(s_con))) {
             PARSE_WARNING("Already configured as slave of %s. Ignoring OVS_PORT=\"%s\"",
                           old_value,
                           v);
@@ -550,7 +550,7 @@ make_connection_setting(const char *file,
     if (v) {
         const char *old_value;
 
-        if ((old_value = nm_setting_connection_get_master(s_con))) {
+        if ((old_value = nm_setting_connection_get_controller(s_con))) {
             PARSE_WARNING("Already configured as slave of %s. Ignoring VRF{_UUID}=\"%s\"",
                           old_value,
                           v);
@@ -4129,6 +4129,10 @@ next:
     v = svGetValueStr(ifcfg, "IEEE_8021X_PHASE2_CA_PATH", &value);
     g_object_set(s_8021x, NM_SETTING_802_1X_PHASE2_CA_PATH, v, NULL);
 
+    nm_clear_g_free(&value);
+    v = svGetValueStr(ifcfg, "IEEE_8021X_OPENSSL_CIPHERS", &value);
+    g_object_set(s_8021x, NM_SETTING_802_1X_OPENSSL_CIPHERS, v, NULL);
+
     g_object_set(s_8021x,
                  NM_SETTING_802_1X_OPTIONAL,
                  svGetValueBoolean(ifcfg, "IEEE_8021X_OPTIONAL", FALSE),
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 b4edefbb..277d0d5f 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
@@ -828,6 +828,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
     _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
+    _KEY_TYPE("IEEE_8021X_OPENSSL_CIPHERS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IEEE_8021X_OPTIONAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IEEE_8021X_PAC_FILE", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IEEE_8021X_PASSWORD", 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 eb9e418a..4e56a258 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
@@ -33,7 +33,7 @@ typedef struct {
     NMSIfcfgKeyTypeFlags key_flags;
 } NMSIfcfgKeyTypeInfo;
 
-extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[264];
+extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[265];
 
 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 617c5ef6..f4598e2d 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
@@ -579,6 +579,10 @@ write_8021x_setting(NMConnection *connection,
                "IEEE_8021X_PIN_FLAGS",
                nm_setting_802_1x_get_pin_flags(s_8021x));
 
+    svSetValueStr(ifcfg,
+                  "IEEE_8021X_OPENSSL_CIPHERS",
+                  nm_setting_802_1x_get_openssl_ciphers(s_8021x));
+
     if (!write_8021x_certs(s_8021x, secrets, blobs, FALSE, ifcfg, error))
         return FALSE;
 
@@ -1138,7 +1142,7 @@ write_wired_setting_impl(NMSettingWired *s_wired, shvarFile *ifcfg, gboolean is_
                   "GENERATE_MAC_ADDRESS_MASK",
                   nm_setting_wired_get_generate_mac_address_mask(s_wired));
 
-    macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired);
+    macaddr_blacklist = nm_setting_wired_get_mac_address_denylist(s_wired);
     if (macaddr_blacklist[0]) {
         gs_free char *blacklist_str = NULL;
 
@@ -2247,7 +2251,7 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg, const cha
     mud_url = nm_setting_connection_get_mud_url(s_con);
     svSetValue(ifcfg, "MUD_URL", mud_url);
 
-    master = nm_setting_connection_get_master(s_con);
+    master = nm_setting_connection_get_controller(s_con);
     if (master) {
         /* The reader prefers the *_UUID variants, however we still try to resolve
          * it into an interface name, so that legacy tooling is not confused. */
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp
new file mode 100644
index 00000000..abfcd6e4
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-dhcp
@@ -0,0 +1,4 @@
+# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile)
+DEVICE=eth0
+HWADDR=00:33:21:98:b9:f1
+BOOTPROTO=ibft
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static
new file mode 100644
index 00000000..99b02e42
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ibft-static
@@ -0,0 +1,4 @@
+# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile)
+DEVICE=eth0
+HWADDR=00:33:21:98:b9:f0
+BOOTPROTO=ibft
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2 b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2
index 27bcbbf9..f32f234a 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-peap-mschapv2
@@ -13,3 +13,4 @@ IEEE_8021X_PEAP_VERSION=1
 IEEE_8021X_PEAP_FORCE_NEW_LABEL=yes
 IEEE_8021X_INNER_AUTH_METHODS=MSCHAPV2
 IEEE_8021X_ANON_IDENTITY=somebody
+IEEE_8021X_OPENSSL_CIPHERS=DEFAULT@SECLEVEL=0
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 1e6b6c25..40a18853 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
@@ -2230,7 +2230,7 @@ test_clear_master(void)
 
     s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION);
 
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "br0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, "bridge");
 
     /* 2. write the connection to a new file */
@@ -2247,7 +2247,7 @@ test_clear_master(void)
                  NULL,
                  NULL);
 
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, NULL);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, NULL);
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NULL);
 
     nmtst_assert_connection_verifies_after_normalization(connection, 0, 0);
@@ -5317,6 +5317,8 @@ test_write_wired_dhcp_8021x_peap_mschapv2(void)
                  "1",
                  NM_SETTING_802_1X_PHASE2_AUTH,
                  "mschapv2",
+                 NM_SETTING_802_1X_OPENSSL_CIPHERS,
+                 "DEFAULT@SECLEVEL=0",
                  NULL);
     nm_setting_802_1x_add_eap_method(s_8021x, "peap");
 
@@ -7668,7 +7670,7 @@ test_read_bridge_component(void)
                                        NULL);
 
     s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "br0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
 
     s_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_BRIDGE_PORT);
@@ -8260,7 +8262,7 @@ test_read_bond_slave(void)
 
     s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION);
 
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "bond0");
 
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
 }
@@ -8317,7 +8319,7 @@ test_read_bond_port(void)
         _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-bond-port", NULL, TYPE_ETHERNET, NULL);
 
     s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond99");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "bond99");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
 
     s_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_BOND_PORT);
@@ -8556,7 +8558,7 @@ test_read_bond_slave_ib(void)
 
     s_con = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_CONNECTION);
 
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "bond0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "bond0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
 }
 
@@ -9096,7 +9098,7 @@ test_read_team_port(gconstpointer user_data)
     g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
                     ==,
                     NM_SETTING_WIRED_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "team0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "team0");
 
     s_team_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_TEAM_PORT);
     g_assert_cmpstr(nm_setting_team_port_get_config(s_team_port), ==, expected_config);
@@ -9224,7 +9226,7 @@ test_read_team_port_empty_config(void)
     g_assert_cmpstr(nm_setting_connection_get_connection_type(s_con),
                     ==,
                     NM_SETTING_WIRED_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "team0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "team0");
 
     /* Normalization adds a team-port setting */
     s_team_port = nmtst_connection_assert_setting(connection, NM_TYPE_SETTING_TEAM_PORT);
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 b26bad69..e3e5f84f 100644
--- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -91,7 +91,7 @@ assert_reread(NMConnection *connection, gboolean normalize_connection, const cha
     reread = keyfile_read_connection_from_file(testfile);
 
     if (!normalize_connection && (s_con = nm_connection_get_setting_connection(connection))
-        && !nm_setting_connection_get_master(s_con)
+        && !nm_setting_connection_get_controller(s_con)
         && !nm_connection_get_setting_proxy(connection)) {
         connection_clone = nmtst_clone_connection(connection);
         connection       = connection_clone;
@@ -2003,7 +2003,7 @@ test_read_bridge_component(void)
     g_assert(s_con);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, expected_id);
     g_assert_cmpstr(nm_setting_connection_get_uuid(s_con), ==, expected_uuid);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, "br0");
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, "br0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
 
     s_wired = nm_connection_get_setting_wired(connection);
diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c
index 9ad4a8f9..7db614a2 100644
--- a/src/core/supplicant/nm-supplicant-config.c
+++ b/src/core/supplicant/nm-supplicant-config.c
@@ -1811,6 +1811,9 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
     value = nm_setting_802_1x_get_anonymous_identity(setting);
     if (!add_string_val(self, value, "anonymous_identity", FALSE, NULL, error))
         return FALSE;
+    value = nm_setting_802_1x_get_openssl_ciphers(setting);
+    if (value && !add_string_val(self, value, "openssl_ciphers", FALSE, NULL, error))
+        return FALSE;
 
     return TRUE;
 }
diff --git a/src/core/supplicant/nm-supplicant-settings-verify.c b/src/core/supplicant/nm-supplicant-settings-verify.c
index 7842365c..c7aaf47d 100644
--- a/src/core/supplicant/nm-supplicant-settings-verify.c
+++ b/src/core/supplicant/nm-supplicant-settings-verify.c
@@ -93,6 +93,7 @@ static const struct Opt opt_table[] = {
     OPT_BYTES("mka_cak", 65536),
     OPT_BYTES("mka_ckn", 65536),
     OPT_BYTES("nai", 0),
+    OPT_BYTES("openssl_ciphers", 0),
     OPT_INT("owe_only", 0, 1),
     OPT_BYTES("pac_file", 0),
     OPT_KEYWORD("pairwise", NM_MAKE_STRV("CCMP", "TKIP", "GCMP-256", "NONE", )),
diff --git a/src/core/tests/config/test-config.c b/src/core/tests/config/test-config.c
index 054b9003..2b27d535 100644
--- a/src/core/tests/config/test-config.c
+++ b/src/core/tests/config/test-config.c
@@ -1076,7 +1076,7 @@ _set_values_intern_atomic_section_2_set(NMConfig            *config,
     g_key_file_set_value(keyfile,
                          NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace",
                          "key2",
-                         " b c\\,  d  ");
+                         " b c\\\\,  d  ");
     *out_expected_changes = NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES
                             | NM_CONFIG_CHANGE_VALUES_INTERN;
 }
diff --git a/src/core/tests/test-dcb.c b/src/core/tests/test-dcb.c
index f85e90f9..33437974 100644
--- a/src/core/tests/test-dcb.c
+++ b/src/core/tests/test-dcb.c
@@ -11,7 +11,7 @@
 
 typedef struct {
     guint       num;
-    const char *cmds[];
+    const char *cmds[16];
 } DcbExpected;
 
 static gboolean
diff --git a/src/core/vpn/nm-vpn-connection.c b/src/core/vpn/nm-vpn-connection.c
index 3dba9ff6..de0c9f71 100644
--- a/src/core/vpn/nm-vpn-connection.c
+++ b/src/core/vpn/nm-vpn-connection.c
@@ -2099,20 +2099,12 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict
                                                  NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4))
                 nm_l3_config_data_add_route(l3cd, addr_family, route, NULL);
         }
-    } else if (IS_IPv4 ? g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter)
-                       : g_variant_lookup(dict,
-                                          NM_VPN_PLUGIN_IP6_CONFIG_ROUTES,
-                                          "a(ayuayu)",
-                                          &var_iter)) {
-        _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter;
-        NMPlatformIPXRoute                                 route              = {};
-        guint32                                            plen;
-        GVariant                                          *next_hop;
-        GVariant                                          *dest;
-        guint32                                            prefix;
-        guint32                                            metric;
+    } else if (IS_IPv4) {
+        if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &var_iter)) {
+            _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = var_iter;
+            NMPlatformIPXRoute                                 route              = {};
+            guint32                                            plen;
 
-        if (IS_IPv4) {
             while (g_variant_iter_next(var_iter, "@au", &v)) {
                 _nm_unused gs_unref_variant GVariant *v_ref_owner = v;
 
@@ -2151,42 +2143,84 @@ _dbus_signal_ip_config_cb(NMVpnConnection *self, int addr_family, GVariant *dict
                     break;
                 }
             }
-        } else {
-            while (
-                g_variant_iter_next(var_iter, "(@ayu@ayu)", &dest, &prefix, &next_hop, &metric)) {
-                _nm_unused gs_unref_variant GVariant *next_hop_ref_owner = next_hop;
-                _nm_unused gs_unref_variant GVariant *dest_ref_owner     = dest;
+        }
+    } else {
+        _nm_unused nm_auto_free_variant_iter GVariantIter *var_iter_ref_owner = NULL;
+        NMPlatformIPXRoute                                 route              = {};
+        guint32                                            prefix;
+        guint32                                            metric;
+        NMOptionBool                                       new_signature = NM_OPTION_BOOL_DEFAULT;
+
+        /* IPv6 and no "preserve-routes" */
+
+        if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &var_iter))
+            new_signature = FALSE;
+        else if (g_variant_lookup(dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayuay)", &var_iter))
+            new_signature = TRUE;
+        else
+            var_iter = NULL;
+
+        var_iter_ref_owner = var_iter;
+
+        while (TRUE) {
+            gs_unref_variant GVariant *next_hop = NULL;
+            gs_unref_variant GVariant *dest     = NULL;
+            gs_unref_variant GVariant *pref_src = NULL;
+
+            if (new_signature == NM_OPTION_BOOL_DEFAULT) {
+                break;
+            } else if (new_signature) {
+                if (!g_variant_iter_next(var_iter,
+                                         "(@ayu@ayu@ay)",
+                                         &dest,
+                                         &prefix,
+                                         &next_hop,
+                                         &metric,
+                                         &pref_src))
+                    break;
+            } else {
+                if (!g_variant_iter_next(var_iter,
+                                         "(@ayu@ayu)",
+                                         &dest,
+                                         &prefix,
+                                         &next_hop,
+                                         &metric))
+                    break;
+            }
 
-                if (prefix > 128)
-                    continue;
+            if (prefix > 128)
+                continue;
 
-                route.r6 = (NMPlatformIP6Route){
-                    .plen       = prefix,
-                    .table_any  = TRUE,
-                    .metric_any = TRUE,
-                    .rt_source  = NM_IP_CONFIG_SOURCE_VPN,
-                };
+            route.r6 = (NMPlatformIP6Route){
+                .plen       = prefix,
+                .table_any  = TRUE,
+                .metric_any = TRUE,
+                .rt_source  = NM_IP_CONFIG_SOURCE_VPN,
+            };
 
-                if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL))
-                    continue;
+            if (!nm_ip_addr_set_from_variant(AF_INET6, &route.r6.network, dest, NULL))
+                continue;
 
-                nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL);
+            if (pref_src
+                && !nm_ip_addr_set_from_variant(AF_INET6, &route.r6.pref_src, pref_src, NULL))
+                continue;
 
-                nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen);
+            nm_ip_addr_set_from_variant(AF_INET6, &route.r6.gateway, next_hop, NULL);
 
-                if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6)
-                    && IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6)
-                    && route.r6.plen == 128) {
-                    /* Ignore host routes to the VPN gateway since NM adds one itself.
-                     * Since NM knows more about the routing situation than the VPN
-                     * server, we want to use the NM created route instead of whatever
-                     * the server provides.
-                     */
-                    continue;
-                }
+            nm_ip6_addr_clear_host_address(&route.r6.network, &route.r6.network, route.r6.plen);
 
-                nm_l3_config_data_add_route_6(l3cd, &route.r6);
+            if (!IN6_IS_ADDR_UNSPECIFIED(&priv->ip_data_6.gw_external.addr6)
+                && IN6_ARE_ADDR_EQUAL(&route.r6.network, &priv->ip_data_6.gw_external.addr6)
+                && route.r6.plen == 128) {
+                /* Ignore host routes to the VPN gateway since NM adds one itself.
+                 * Since NM knows more about the routing situation than the VPN
+                 * server, we want to use the NM created route instead of whatever
+                 * the server provides.
+                 */
+                continue;
             }
+
+            nm_l3_config_data_add_route_6(l3cd, &route.r6);
         }
     }
 
diff --git a/src/libnm-base/README.md b/src/libnm-base/README.md
new file mode 100644
index 00000000..8eb2119a
--- /dev/null
+++ b/src/libnm-base/README.md
@@ -0,0 +1,20 @@
+libnm-base
+==========
+
+A static helper library with network/NetworkManager specific
+code.
+
+Contrary to libnm-glib-aux, this does not contain general purpose
+helpers, but code that is more specific about NetworkManager.
+
+This is the most low-level dependency of this kind. Most NetworkManager
+specific code will directly or indirectly link with this.
+
+As this is a static library, there is no problem with dragging this into your
+binary/library, if your application already depends on libnm-glib-aux (and glib).
+
+Dependencies:
+
+  - glib
+  - [../libnm-std-aux/](../libnm-std-aux/)
+  - [../libnm-glib-aux/](../libnm-glib-aux/)
diff --git a/src/libnm-base/nm-base.h b/src/libnm-base/nm-base.h
index e1cc2733..4b1bff54 100644
--- a/src/libnm-base/nm-base.h
+++ b/src/libnm-base/nm-base.h
@@ -147,8 +147,8 @@ typedef enum {
     _NM_ETHTOOL_ID_NUM = (_NM_ETHTOOL_ID_LAST - _NM_ETHTOOL_ID_FIRST + 1),
 } NMEthtoolID;
 
-#define _NM_ETHTOOL_ID_FEATURE_AS_IDX(ethtool_id)  ((ethtool_id) -_NM_ETHTOOL_ID_FEATURE_FIRST)
-#define _NM_ETHTOOL_ID_COALESCE_AS_IDX(ethtool_id) ((ethtool_id) -_NM_ETHTOOL_ID_COALESCE_FIRST)
+#define _NM_ETHTOOL_ID_FEATURE_AS_IDX(ethtool_id)  ((ethtool_id) - _NM_ETHTOOL_ID_FEATURE_FIRST)
+#define _NM_ETHTOOL_ID_COALESCE_AS_IDX(ethtool_id) ((ethtool_id) - _NM_ETHTOOL_ID_COALESCE_FIRST)
 
 typedef enum {
     NM_ETHTOOL_TYPE_UNKNOWN,
diff --git a/src/libnm-client-impl/README.md b/src/libnm-client-impl/README.md
new file mode 100644
index 00000000..1ec481c1
--- /dev/null
+++ b/src/libnm-client-impl/README.md
@@ -0,0 +1,14 @@
+libnm-client-impl
+=================
+
+libnm is NetworkManager's client API.
+This API consists of two parts:
+
+- the handling of connections (`NMConnection`), implemented
+  by libnm-core-impl.
+- the caching of D-Bus API (`NMClient`), implemented by
+  libnm-client-impl.
+
+This directory contains the implementation of the second part.
+As such, it will be statically linked with libnm-core-impl
+to make libnm. Also, it cannot be used by the daemon.
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index 5442377a..666a22a8 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -1958,6 +1958,7 @@ global:
 	nm_ethtool_optname_is_eee;
 	nm_setting_connection_get_autoconnect_ports;
 	nm_setting_connection_get_controller;
+	nm_setting_connection_get_down_on_poweroff;
 	nm_setting_connection_get_port_type;
 	nm_setting_generic_get_device_handler;
 	nm_setting_get_enum_property_type;
@@ -1977,3 +1978,27 @@ global:
 	nm_sriov_eswitch_inline_mode_get_type;
 	nm_sriov_eswitch_mode_get_type;
 } libnm_1_44_0;
+
+libnm_1_48_0 {
+global:
+	nm_setting_connection_down_on_poweroff_get_type;
+	nm_setting_connection_get_down_on_poweroff;
+	nm_setting_ip6_config_get_temp_preferred_lifetime;
+	nm_setting_ip6_config_get_temp_valid_lifetime;
+	nm_setting_ip_config_get_dhcp_send_release;
+	nm_setting_wired_add_mac_denylist_item;
+	nm_setting_wired_clear_mac_denylist_items;
+	nm_setting_wired_get_mac_address_denylist;
+	nm_setting_wired_get_mac_denylist_item;
+	nm_setting_wired_get_num_mac_denylist_items;
+	nm_setting_wired_remove_mac_denylist_item;
+	nm_setting_wired_remove_mac_denylist_item_by_value;
+	nm_setting_wireless_add_mac_denylist_item;
+	nm_setting_wireless_clear_mac_denylist_items;
+	nm_setting_wireless_get_mac_address_denylist;
+	nm_setting_wireless_get_mac_denylist_item;
+	nm_setting_wireless_get_num_mac_denylist_items;
+	nm_setting_wireless_remove_mac_denylist_item;
+	nm_setting_wireless_remove_mac_denylist_item_by_value;
+	nm_setting_802_1x_get_openssl_ciphers;
+} libnm_1_46_0;
diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build
index 79ac9559..3dd2338a 100644
--- a/src/libnm-client-impl/meson.build
+++ b/src/libnm-client-impl/meson.build
@@ -158,7 +158,6 @@ if enable_introspection
     namespace: 'NM',
     identifier_prefix: nm_id_prefix,
     symbol_prefix: nm_id_prefix.to_lower(),
-    header: 'NetworkManager.h',
     export_packages: libnm_name,
     extra_args: [
       '-DNETWORKMANAGER_COMPILATION',
@@ -166,20 +165,20 @@ if enable_introspection
     install: true,
   )
 
-  gi_typelib_path = run_command('printenv', 'GI_TYPELIB_PATH').stdout()
+  gi_typelib_path = run_command('printenv', 'GI_TYPELIB_PATH', check: false).stdout()
   if gi_typelib_path != ''
     gi_typelib_path = ':' + gi_typelib_path
   endif
   gi_typelib_path = meson.current_build_dir() + gi_typelib_path
 
-  ld_library_path = run_command('printenv', 'LD_LIBRARY_PATH').stdout()
+  ld_library_path = run_command('printenv', 'LD_LIBRARY_PATH', check: false).stdout()
   if ld_library_path != ''
     ld_library_path = ':' + ld_library_path
   endif
   ld_library_path = meson.current_build_dir() + ld_library_path
 
-  gen_infos_cmd = join_paths(meson.source_root(), 'tools', 'generate-docs-nm-property-infos.py')
-  gen_gir_cmd = join_paths(meson.source_root(), 'tools', 'generate-docs-nm-settings-docs-gir.py')
+  gen_infos_cmd = files(source_root / 'tools' / 'generate-docs-nm-property-infos.py')
+  gen_gir_cmd = files(source_root / 'tools' / 'generate-docs-nm-settings-docs-gir.py')
 
   names = [ 'dbus', 'nmcli', 'keyfile' ]
   if enable_ifcfg_rh
@@ -191,7 +190,7 @@ if enable_introspection
       input: [gen_infos_cmd, libnm_gir[0]] + libnm_core_settings_sources,
       output: 'nm-property-infos-' + name + '.xml',
       command: [
-        python.path(),
+        python_path,
         gen_infos_cmd,
         name,
         '@OUTPUT@',
@@ -207,7 +206,7 @@ if enable_introspection
         'env',
         'GI_TYPELIB_PATH=' + gi_typelib_path,
         'LD_LIBRARY_PATH=' + ld_library_path,
-        python.path(),
+        python_path,
         gen_gir_cmd,
         '--lib-path', meson.current_build_dir(),
         '--gir', libnm_gir[0],
diff --git a/src/libnm-client-impl/nm-conn-utils.c b/src/libnm-client-impl/nm-conn-utils.c
index 08f8403c..e548a961 100644
--- a/src/libnm-client-impl/nm-conn-utils.c
+++ b/src/libnm-client-impl/nm-conn-utils.c
@@ -194,8 +194,8 @@ nm_conn_wireguard_import(const char *filename, GError **error)
      * This code here instead generates a NetworkManager connection profile so that
      * NetworkManager will apply a similar configuration when later activating the profile. */
 
-#define _TABLE_AUTO ((gint64) -1)
-#define _TABLE_OFF  ((gint64) -2)
+#define _TABLE_AUTO ((gint64) - 1)
+#define _TABLE_OFF  ((gint64) - 2)
 
     data_table = _TABLE_AUTO;
 
diff --git a/src/libnm-client-impl/nm-device-ethernet.c b/src/libnm-client-impl/nm-device-ethernet.c
index 42b7262d..f51e4992 100644
--- a/src/libnm-client-impl/nm-device-ethernet.c
+++ b/src/libnm-client-impl/nm-device-ethernet.c
@@ -195,7 +195,7 @@ connection_compatible(NMDevice *device, NMConnection *connection, GError **error
     if (s_wired) {
         const char        *perm_addr, *s_mac;
         gboolean           try_mac = TRUE;
-        const char *const *mac_blacklist;
+        const char *const *mac_denylist;
         int                i;
 
         /* Check s390 subchannels */
@@ -232,20 +232,20 @@ connection_compatible(NMDevice *device, NMConnection *connection, GError **error
                 return FALSE;
             }
 
-            /* Check for MAC address blacklist */
-            mac_blacklist = nm_setting_wired_get_mac_address_blacklist(s_wired);
-            for (i = 0; mac_blacklist[i]; i++) {
-                if (!nm_utils_hwaddr_valid(mac_blacklist[i], ETH_ALEN)) {
+            /* Check for MAC address denylist */
+            mac_denylist = nm_setting_wired_get_mac_address_denylist(s_wired);
+            for (i = 0; mac_denylist[i]; i++) {
+                if (!nm_utils_hwaddr_valid(mac_denylist[i], ETH_ALEN)) {
                     g_warn_if_reached();
                     g_set_error(error,
                                 NM_DEVICE_ERROR,
                                 NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
                                 _("Invalid MAC in the blacklist: %s."),
-                                mac_blacklist[i]);
+                                mac_denylist[i]);
                     return FALSE;
                 }
 
-                if (nm_utils_hwaddr_matches(mac_blacklist[i], -1, perm_addr, -1)) {
+                if (nm_utils_hwaddr_matches(mac_denylist[i], -1, perm_addr, -1)) {
                     g_set_error(error,
                                 NM_DEVICE_ERROR,
                                 NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
diff --git a/src/libnm-client-impl/tests/meson.build b/src/libnm-client-impl/tests/meson.build
index 0c0e188b..42e9883e 100644
--- a/src/libnm-client-impl/tests/meson.build
+++ b/src/libnm-client-impl/tests/meson.build
@@ -50,9 +50,9 @@ if enable_introspection
     'check-local-libnm-gir',
     python,
     args: [
-      join_paths(meson.source_root(), 'src', 'libnm-client-impl', 'tests', 'test-gir.py'),
+      join_paths(source_root, 'src', 'libnm-client-impl', 'tests', 'test-gir.py'),
       '--gir', libnm_gir[0],
-      '--ver', join_paths(meson.source_root(), 'src', 'libnm-client-impl', 'libnm.ver'),
+      '--ver', join_paths(source_root, 'src', 'libnm-client-impl', 'libnm.ver'),
     ],
   )
 endif
diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c
index 59774f96..687e6c17 100644
--- a/src/libnm-client-impl/tests/test-libnm.c
+++ b/src/libnm-client-impl/tests/test-libnm.c
@@ -2397,10 +2397,10 @@ _do_read_vpn_details_impl1(const char              *file,
 
         g_print(">>>> n_read=%zd;  \"%s\"",
                 n_read,
-                n_read > 0 ? (
-                    ss = nm_utils_buf_utf8safe_escape_cp(read_buf,
-                                                         n_read,
-                                                         NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL))
+                n_read > 0 ? (ss = nm_utils_buf_utf8safe_escape_cp(
+                                  read_buf,
+                                  n_read,
+                                  NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL))
                            : "");
     }
 
diff --git a/src/libnm-client-impl/tests/test-nm-client.c b/src/libnm-client-impl/tests/test-nm-client.c
index 216e3e42..3d527324 100644
--- a/src/libnm-client-impl/tests/test-nm-client.c
+++ b/src/libnm-client-impl/tests/test-nm-client.c
@@ -1023,7 +1023,7 @@ _test_connection_invalid_find_connections(gpointer element, gpointer needle, gpo
     G_STMT_START                                                                          \
     {                                                                                     \
         g_assert_cmpint(idx[i], >=, 0);                                                   \
-        g_assert(path##i &&*path##i);                                                     \
+        g_assert(path##i && *path##i);                                                    \
         g_assert(NM_IS_REMOTE_CONNECTION(connections->pdata[idx[i]]));                    \
         g_assert_cmpstr(nm_connection_get_path(connections->pdata[idx[i]]), ==, path##i); \
     }                                                                                     \
diff --git a/src/libnm-client-public/README.md b/src/libnm-client-public/README.md
new file mode 100644
index 00000000..a4ecb066
--- /dev/null
+++ b/src/libnm-client-public/README.md
@@ -0,0 +1,16 @@
+libnm-client-public
+===================
+
+libnm is NetworkManager's client API. It has a public API.
+This API consists of two parts:
+
+- the handling of connections (`NMConnection`), implemented
+  by libnm-core-impl.
+- the caching of D-Bus API (`NMClient`), implemented by
+  libnm-client-impl.
+
+This directory contains public headers that are used by libnm
+users. As such, it's the `NMClient` part of libnm-core-public.
+
+These headers are usable to any libnm client application and
+to libnm itself. But not to libnm-core-impl or the daemon.
diff --git a/src/libnm-client-test/README.md b/src/libnm-client-test/README.md
new file mode 100644
index 00000000..f06220b4
--- /dev/null
+++ b/src/libnm-client-test/README.md
@@ -0,0 +1,11 @@
+libnm-client-test
+=================
+
+A static helper library that is used by unit tests
+on top of libnm. Mostly it's D-Bus helpers.
+
+It has no purpose in non-test code.
+
+Unit tests may not dynamically link with libnm. They
+may also statically link with the relevant parts of libnm,
+and still be able to use this helper.
diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
index 146f9282..2b231983 100644
--- a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
+++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
@@ -103,6 +103,11 @@
                   gprop-type="gchararray"
                   />
         <property name="mac-address-blacklist"
+                  is-deprecated="1"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="mac-address-denylist"
                   dbus-type="as"
                   gprop-type="GStrv"
                   />
@@ -306,6 +311,10 @@
                   dbus-type="s"
                   gprop-type="gchararray"
                   />
+        <property name="openssl-ciphers"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
         <property name="optional"
                   dbus-type="b"
                   gprop-type="gboolean"
@@ -487,6 +496,11 @@
                   gprop-type="gchararray"
                   />
         <property name="mac-address-blacklist"
+                  is-deprecated="1"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="mac-address-denylist"
                   dbus-type="as"
                   gprop-type="GStrv"
                   />
@@ -792,6 +806,10 @@
                   dbus-type="i"
                   gprop-type="gint"
                   />
+        <property name="down-on-poweroff"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
         <property name="gateway-ping-timeout"
                   dbus-type="u"
                   gprop-type="guint"
@@ -1596,6 +1614,10 @@
                   dbus-type="b"
                   gprop-type="gboolean"
                   />
+        <property name="dhcp-send-release"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
         <property name="dhcp-timeout"
                   dbus-type="i"
                   gprop-type="gint"
@@ -1735,6 +1757,10 @@
                   dbus-type="b"
                   gprop-type="gboolean"
                   />
+        <property name="dhcp-send-release"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
         <property name="dhcp-timeout"
                   dbus-type="i"
                   gprop-type="gint"
@@ -1822,6 +1848,14 @@
         <property name="routing-rules"
                   dbus-type="aa{sv}"
                   />
+        <property name="temp-preferred-lifetime"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="temp-valid-lifetime"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
         <property name="token"
                   dbus-type="s"
                   gprop-type="gchararray"
diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c
index 33360d04..15b489d6 100644
--- a/src/libnm-core-impl/nm-connection.c
+++ b/src/libnm-core-impl/nm-connection.c
@@ -1093,7 +1093,7 @@ _normalize_connection_slave_type(NMConnection *self)
 
     if (!s_con)
         return FALSE;
-    if (!nm_setting_connection_get_master(s_con))
+    if (!nm_setting_connection_get_controller(s_con))
         return FALSE;
 
     slave_type = nm_setting_connection_get_port_type(s_con);
@@ -1175,7 +1175,7 @@ _supports_addr_family(NMConnection *self, int family)
         && (nm_streq0(nm_setting_connection_get_port_type(s_con), NM_SETTING_VRF_SETTING_NAME)))
         return TRUE;
 
-    return !nm_setting_connection_get_master(nm_connection_get_setting_connection(self));
+    return !nm_setting_connection_get_controller(nm_connection_get_setting_connection(self));
 }
 
 static gboolean
diff --git a/src/libnm-core-impl/nm-setting-6lowpan.c b/src/libnm-core-impl/nm-setting-6lowpan.c
index 7066c3e6..d318ab00 100644
--- a/src/libnm-core-impl/nm-setting-6lowpan.c
+++ b/src/libnm-core-impl/nm-setting-6lowpan.c
@@ -95,7 +95,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
             slave_type = nm_setting_connection_get_port_type(s_con);
             if (!g_strcmp0(slave_type, NM_SETTING_6LOWPAN_SETTING_NAME))
-                master = nm_setting_connection_get_master(s_con);
+                master = nm_setting_connection_get_controller(s_con);
 
             if (master && g_strcmp0(priv->parent, master) != 0) {
                 g_set_error(error,
diff --git a/src/libnm-core-impl/nm-setting-8021x.c b/src/libnm-core-impl/nm-setting-8021x.c
index 945fd6d3..4ea60729 100644
--- a/src/libnm-core-impl/nm-setting-8021x.c
+++ b/src/libnm-core-impl/nm-setting-8021x.c
@@ -131,7 +131,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSetting8021x,
                              PROP_PIN_FLAGS,
                              PROP_SYSTEM_CA_CERTS,
                              PROP_OPTIONAL,
-                             PROP_AUTH_TIMEOUT, );
+                             PROP_AUTH_TIMEOUT,
+                             PROP_OPENSSL_CIPHERS, );
 
 typedef struct {
     GSList *eap; /* GSList of strings */
@@ -168,6 +169,7 @@ typedef struct {
     char   *private_key_password;
     GBytes *phase2_private_key;
     char   *phase2_private_key_password;
+    char   *openssl_ciphers;
     guint   ca_cert_password_flags;
     guint   client_cert_password_flags;
     guint   phase2_ca_cert_password_flags;
@@ -2498,6 +2500,24 @@ nm_setting_802_1x_get_optional(NMSetting8021x *setting)
     return NM_SETTING_802_1X_GET_PRIVATE(setting)->optional;
 }
 
+/**
+ * nm_setting_802_1x_get_openssl_ciphers:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the openssl_ciphers configuration for wpa_supplicant.
+ *
+ * Returns: cipher string for tls setup in wpa_supplicant.
+ *
+ * Since: 1.48
+ **/
+const char *
+nm_setting_802_1x_get_openssl_ciphers(NMSetting8021x *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_802_1X(setting), NULL);
+
+    return NM_SETTING_802_1X_GET_PRIVATE(setting)->openssl_ciphers;
+}
+
 /*****************************************************************************/
 
 static void
@@ -3315,6 +3335,19 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Setting this property directly is discouraged; use the
      * nm_setting_802_1x_set_ca_cert() function instead.
      **/
+    /* ---nmcli---
+     * property: ca-cert
+     * description:
+     *   Contains the path to the CA certificate if used by the EAP method
+     *   specified in the 802-1x.eap property.
+     *
+     *   This property can be unset even if the EAP method supports CA certificates,
+     *   but this allows man-in-the-middle attacks and is NOT recommended.
+     *
+     *   Note that enabling 802-1x.system-ca-certs will override this
+     *   setting to use the built-in path, if the built-in path is not a directory.
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: ca-cert
      * variable: IEEE_8021X_CA_CERT(+)
@@ -3505,6 +3538,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Setting this property directly is discouraged; use the
      * nm_setting_802_1x_set_client_cert() function instead.
      **/
+    /* ---nmcli---
+     * property: client-cert
+     * description:
+     *   Contains the path to the client certificate if used by the EAP method
+     *   specified in the 802-1x.eap property.
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: client-cert
      * variable: IEEE_8021X_CLIENT_CERT(+)
@@ -3745,6 +3785,20 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Setting this property directly is discouraged; use the
      * nm_setting_802_1x_set_phase2_ca_cert() function instead.
      **/
+    /* ---nmcli---
+     * property: phase2-ca-cert
+     * description:
+     *   Contains the path to the "phase 2" CA certificate if used by the EAP
+     *   method specified in the 802-1x.phase2-auth or 802-1x.phase2-autheap
+     *   properties.
+     *
+     *   This property can be unset even if the EAP method supports CA certificates,
+     *   but this allows man-in-the-middle attacks and is NOT recommended.
+     *
+     *   Note that enabling 802-1x.system-ca-certs will override this
+     *   setting to use the built-in path, if the built-in path is not a directory.
+     * ---end---
+     */
     _nm_setting_property_define_direct_bytes(properties_override,
                                              obj_properties,
                                              NM_SETTING_802_1X_PHASE2_CA_CERT,
@@ -3933,6 +3987,14 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Setting this property directly is discouraged; use the
      * nm_setting_802_1x_set_phase2_client_cert() function instead.
      **/
+    /* ---nmcli---
+     * property: phase2-client-cert
+     * description:
+     *   Contains the path to the "phase 2" client certificate if used by the EAP
+     *   method specified in the 802-1x.phase2-auth or 802-1x.phase2-autheap
+     *   properties.
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: phase2-client-cert
      * variable: IEEE_8021X_INNER_CLIENT_CERT(+)
@@ -4096,6 +4158,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * private key password to prevent unauthorized access to unencrypted
      * private key data.
      **/
+    /* ---nmcli---
+     * property: private-key
+     * description:
+     *   The path to the private key when the 802-1.eap property is set to "tls".
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: private-key
      * variable: IEEE_8021X_PRIVATE_KEY(+)
@@ -4121,6 +4189,14 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * secrets to NetworkManager; it is generally set automatically when setting
      * the private key by the nm_setting_802_1x_set_private_key() function.
      **/
+    /* ---nmcli---
+     * property: private-key-password
+     * description:
+     *   The password used to decrypt the private key specified in the
+     *   802-1x.private-key property. This is normally used by secret agents,
+     *   not directly by users.
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: private-key-password
      * variable: IEEE_8021X_PRIVATE_KEY_PASSWORD(+)
@@ -4183,6 +4259,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Setting this property directly is discouraged; use the
      * nm_setting_802_1x_set_phase2_private_key() function instead.
      **/
+    /* ---nmcli---
+     * property: phase2-private-key
+     * description:
+     *   The path to the "phase 2" inner private key when the 802-1x.phase2-auth
+     *   or 802-1x.phase2-autheap property is set to "tls".
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: phase2-private-key
      * variable: IEEE_8021X_INNER_PRIVATE_KEY(+)
@@ -4208,6 +4291,14 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * the private key by the nm_setting_802_1x_set_phase2_private_key()
      * function.
      **/
+    /* ---nmcli---
+     * property: phase2-private-key-password
+     * description:
+     *   The password used to decrypt the "phase 2" private key specified in the
+     *   802-1x.phase2-private-key property. This is normally used by secret agents,
+     *   not directly by users.
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: phase2-private-key-password
      * variable: IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD(+)
@@ -4364,6 +4455,30 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
                                                NMSetting8021xPrivate,
                                                optional);
 
+    /**
+     * NMSetting8021x:openssl-ciphers:
+     *
+     * Define openssl_ciphers for wpa_supplicant. Openssl sometimes moves ciphers
+     * among SECLEVELs, thus compiled-in default value in wpa_supplicant
+     * (as modified by some linux distributions) sometimes prevents
+     * to connect to old servers that do not support new protocols.
+     *
+     * Since: 1.48
+     **/
+    /* ---ifcfg-rh---
+     * property: openssl-ciphers
+     * variable: IEEE_8021X_OPENSSL_CIPHERS(+)
+     * description: Cipher string for tls setup of wpa_supplicant.
+     * ---end---
+     */
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_OPENSSL_CIPHERS,
+                                              PROP_OPENSSL_CIPHERS,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              openssl_ciphers);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
     _nm_setting_class_commit(setting_class,
diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c
index 7c58c84f..30a9509b 100644
--- a/src/libnm-core-impl/nm-setting-connection.c
+++ b/src/libnm-core-impl/nm-setting-connection.c
@@ -73,7 +73,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection,
                              PROP_AUTH_RETRIES,
                              PROP_WAIT_DEVICE_TIMEOUT,
                              PROP_MUD_URL,
-                             PROP_WAIT_ACTIVATION_DELAY, );
+                             PROP_WAIT_ACTIVATION_DELAY,
+                             PROP_DOWN_ON_POWEROFF, );
 
 typedef struct {
     GArray     *permissions;
@@ -89,6 +90,7 @@ typedef struct {
     char       *mud_url;
     guint64     timestamp;
     int         autoconnect_ports;
+    int         down_on_poweroff;
     int         metered;
     gint32      autoconnect_priority;
     gint32      autoconnect_retries;
@@ -713,7 +715,7 @@ nm_setting_connection_get_zone(NMSettingConnection *setting)
  * Returns: interface name of the master device or UUID of the master
  * connection.
  *
- * Deprecated: 1.46. Use nm_setting_connection_get_controller() instead which
+ * Deprecated: 1.46. Use nm_setting_connection_get_master() instead which
  * is just an alias.
  */
 const char *
@@ -829,6 +831,26 @@ nm_setting_connection_get_wait_activation_delay(NMSettingConnection *setting)
 }
 
 /**
+ * nm_setting_connection_get_down_on_poweroff:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the %NM_SETTING_CONNECTION_DOWN_ON_POWEROFF property.
+ *
+ * Returns: whether the connection will be brought down before the system
+ * is powered off.
+ *
+ * Since: 1.48
+ */
+NMSettingConnectionDownOnPoweroff
+nm_setting_connection_get_down_on_poweroff(NMSettingConnection *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting),
+                         NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT);
+
+    return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->down_on_poweroff;
+}
+
+/**
  * nm_setting_connection_get_autoconnect_ports:
  * @setting: the #NMSettingConnection
  *
@@ -2643,9 +2665,9 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * If -1 (default) is set, global connection.autoconnect-slaves is read to
      * determine the real value. If it is default as well, this fallbacks to 0.
      *
-     * Since: 1.2
-     *
      * Deprecated 1.46. Use #NMSettingConnection:autoconnect-ports instead, this is just an alias.
+     *
+     * Since: 1.2
      **/
     /* ---ifcfg-rh---
      * property: autoconnect-slaves
@@ -2729,6 +2751,7 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
                                             NM_SETTING_CONNECTION_SECONDARIES,
                                             PROP_SECONDARIES,
                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                            NULL,
                                             NMSettingConnectionPrivate,
                                             secondaries);
 
@@ -2852,11 +2875,12 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * for the connection, "no" (0) disable mDNS for the interface, "resolve"
      * (1) do not register hostname but allow resolving of mDNS host names
      * and "default" (-1) to allow lookup of a global default in NetworkManager.conf.
-     * If unspecified, "default" ultimately depends on the DNS plugin (which
-     * for systemd-resolved currently means "no").
+     * If unspecified, "default" ultimately depends on the DNS plugin.
      *
      * This feature requires a plugin which supports mDNS. Otherwise, the
-     * setting has no effect. One such plugin is dns-systemd-resolved.
+     * setting has no effect. Currently the only supported DNS plugin is
+     * systemd-resolved. For systemd-resolved, the default is configurable via
+     * MulticastDNS= setting in resolved.conf.
      *
      * Since: 1.12
      **/
@@ -3157,6 +3181,29 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
                                              NMSettingConnectionPrivate,
                                              wait_activation_delay);
 
+    /**
+     * NMSettingConnection:down-on-poweroff:
+     *
+     *
+     * Whether the connection will be brought down before the system is powered
+     * off.  The default value is %NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT. When
+     * the default value is specified, then the global value from
+     * NetworkManager configuration is looked up, if not set, it is considered
+     * as %NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO.
+     *
+     * Since: 1.48
+     **/
+    _nm_setting_property_define_direct_enum(properties_override,
+                                            obj_properties,
+                                            NM_SETTING_CONNECTION_DOWN_ON_POWEROFF,
+                                            PROP_DOWN_ON_POWEROFF,
+                                            NM_TYPE_SETTING_CONNECTION_DOWN_ON_POWEROFF,
+                                            NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT,
+                                            NM_SETTING_PARAM_NONE,
+                                            NULL,
+                                            NMSettingConnectionPrivate,
+                                            down_on_poweroff);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
     _nm_setting_class_commit(setting_class,
diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c
index 02334b54..e79f25a8 100644
--- a/src/libnm-core-impl/nm-setting-ip-config.c
+++ b/src/libnm-core-impl/nm-setting-ip-config.c
@@ -4004,7 +4004,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig,
                              PROP_DHCP_IAID,
                              PROP_DHCP_REJECT_SERVERS,
                              PROP_AUTO_ROUTE_EXT_GW,
-                             PROP_REPLACE_LOCAL_RULE, );
+                             PROP_REPLACE_LOCAL_RULE,
+                             PROP_DHCP_SEND_RELEASE, );
 
 G_DEFINE_ABSTRACT_TYPE(NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING)
 
@@ -5463,6 +5464,22 @@ nm_setting_ip_config_get_replace_local_rule(NMSettingIPConfig *setting)
     return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->replace_local_rule;
 }
 
+/**
+ * nm_setting_ip_config_get_dhcp_send_release:
+ * @setting: the #NMSettingIPConfig
+ *
+ * Returns: the #NMSettingIPConfig:dhcp-send-release property of the setting
+ *
+ * Since: 1.48
+ **/
+NMTernary
+nm_setting_ip_config_get_dhcp_send_release(NMSettingIPConfig *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NM_TERNARY_DEFAULT);
+
+    return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dhcp_send_release;
+}
+
 static gboolean
 verify_label(const char *label)
 {
@@ -6152,6 +6169,14 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family)
 
     _nm_properties_override_gobj(
         properties_override,
+        obj_properties[PROP_DHCP_SEND_RELEASE],
+        &nm_sett_info_propert_type_direct_enum,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(int, NMSettingIPConfigPrivate, dhcp_send_release),
+        .direct_data.enum_gtype = NM_TYPE_TERNARY);
+
+    _nm_properties_override_gobj(
+        properties_override,
         obj_properties[PROP_DNS_SEARCH],
         &nm_sett_info_propert_type_direct_strv,
         .direct_offset =
@@ -6903,5 +6928,23 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
                           NM_TERNARY_DEFAULT,
                           G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
 
+    /**
+     * NMSettingIPConfig:dhcp-send-release:
+     *
+     * Whether the DHCP client will send RELEASE message when
+     * bringing the connection down. The default value is %NM_TERNARY_DEFAULT.
+     * When the default value is specified, then the global value from NetworkManager
+     * configuration is looked up, if not set, it is considered as %FALSE.
+     *
+     * Since: 1.48
+     */
+    obj_properties[PROP_DHCP_SEND_RELEASE] =
+        g_param_spec_enum(NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE,
+                          "",
+                          "",
+                          NM_TYPE_TERNARY,
+                          NM_TERNARY_DEFAULT,
+                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c
index 42bb2571..335700bf 100644
--- a/src/libnm-core-impl/nm-setting-ip6-config.c
+++ b/src/libnm-core-impl/nm-setting-ip6-config.c
@@ -40,6 +40,8 @@
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_IP6_PRIVACY,
+                                  PROP_TEMP_VALID_LIFETIME,
+                                  PROP_TEMP_PREFERRED_LIFETIME,
                                   PROP_ADDR_GEN_MODE,
                                   PROP_TOKEN,
                                   PROP_DHCP_DUID,
@@ -54,6 +56,8 @@ typedef struct {
     char   *dhcp_duid;
     char   *dhcp_pd_hint;
     int     ip6_privacy;
+    gint32  temp_valid_lifetime;
+    gint32  temp_preferred_lifetime;
     gint32  addr_gen_mode;
     gint32  ra_timeout;
     guint32 mtu;
@@ -98,6 +102,44 @@ nm_setting_ip6_config_get_ip6_privacy(NMSettingIP6Config *setting)
 }
 
 /**
+ * nm_setting_ip6_config_get_temp_valid_lifetime:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:temp-valid-lifetime
+ * property.
+ *
+ * Returns: The valid lifetime of autogenerated temporary addresses.
+ *
+ * Since: 1.48
+ **/
+gint32
+nm_setting_ip6_config_get_temp_valid_lifetime(NMSettingIP6Config *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_IP6_CONFIG(setting), 0);
+
+    return NM_SETTING_IP6_CONFIG_GET_PRIVATE(setting)->temp_valid_lifetime;
+}
+
+/**
+ * nm_setting_ip6_config_get_temp_preferred_lifetime:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:temp-preferred-lifetime
+ * property.
+ *
+ * Returns: The preferred lifetime of autogenerated temporary addresses.
+ *
+ * Since: 1.48
+ **/
+gint32
+nm_setting_ip6_config_get_temp_preferred_lifetime(NMSettingIP6Config *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_IP6_CONFIG(setting), 0);
+
+    return NM_SETTING_IP6_CONFIG_GET_PRIVATE(setting)->temp_preferred_lifetime;
+}
+
+/**
  * nm_setting_ip6_config_get_dhcp_pd_hint:
  * @setting: the #NMSettingIP6Config
  *
@@ -921,11 +963,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary
      * addresses).
      *
-     * Having a per-connection setting set to "-1" (unknown) means fallback to
-     * global configuration "ipv6.ip6-privacy".
-     *
-     * If also global configuration is unspecified or set to "-1", fallback to read
-     * "/proc/sys/net/ipv6/conf/default/use_tempaddr".
+     * If set to "-1" (unknown) for a connection, the value is taken from the
+     * global "ipv6.ip6-privacy" setting. If the global setting is unspecified
+     * or also set to "-1", the value is set from the original value of
+     * "/proc/sys/net/ipv6/conf/<iface>/use_tempaddr" from before NetworkManager
+     * started.
      *
      * Note that this setting is distinct from the Stable Privacy addresses
      * that can be enabled with the "addr-gen-mode" property's "stable-privacy"
@@ -953,6 +995,54 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
                                                  ip6_privacy);
 
     /**
+     * NMSettingIP6Config:temp-valid-lifetime:
+     *
+     * The valid lifetime of autogenerated temporary addresses, in seconds.
+     *
+     * If set to "0" (unknown) for a connection, the value is taken from the
+     * global "ipv6.temp-valid-lifetime" setting. If the global setting is
+     * unspecified or also set to "0", the value is set from the original value
+     * of "/proc/sys/net/ipv6/conf/<iface>/temp_valid_lft" from before
+     * NetworkManager started.
+     *
+     * Since: 1.48
+     **/
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_IP6_CONFIG_TEMP_VALID_LIFETIME,
+                                             PROP_TEMP_VALID_LIFETIME,
+                                             0,
+                                             G_MAXINT32,
+                                             0,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingIP6ConfigPrivate,
+                                             temp_valid_lifetime);
+
+    /**
+     * NMSettingIP6Config:temp-preferred-lifetime:
+     *
+     * The preferred lifetime of autogenerated temporary addresses, in seconds.
+     *
+     * If set to "0" (unknown) for a connection, the value is taken from the
+     * global "ipv6.temp-preferred-lifetime" setting. If the global setting is
+     * unspecified or also set to "0", the value is set from the original value
+     * of "/proc/sys/net/ipv6/conf/<iface>/temp_prefered_lft" from before
+     * NetworkManager started.
+     *
+     * Since: 1.48
+     **/
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_IP6_CONFIG_TEMP_PREFERRED_LIFETIME,
+                                             PROP_TEMP_PREFERRED_LIFETIME,
+                                             0,
+                                             G_MAXINT32,
+                                             0,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingIP6ConfigPrivate,
+                                             temp_preferred_lifetime);
+
+    /**
      * NMSettingIP6Config:addr-gen-mode:
      *
      * Configure the method for creating the IPv6 interface identifier of
diff --git a/src/libnm-core-impl/nm-setting-loopback.c b/src/libnm-core-impl/nm-setting-loopback.c
index b329c74a..2dc26ccc 100644
--- a/src/libnm-core-impl/nm-setting-loopback.c
+++ b/src/libnm-core-impl/nm-setting-loopback.c
@@ -126,7 +126,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
         if ((s_con = nm_connection_get_setting_connection(connection))) {
             if (nm_setting_connection_get_port_type(s_con)
-                || nm_setting_connection_get_master(s_con)) {
+                || nm_setting_connection_get_controller(s_con)) {
                 g_set_error(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/src/libnm-core-impl/nm-setting-macsec.c b/src/libnm-core-impl/nm-setting-macsec.c
index 763d306b..5732fa07 100644
--- a/src/libnm-core-impl/nm-setting-macsec.c
+++ b/src/libnm-core-impl/nm-setting-macsec.c
@@ -324,7 +324,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
                 slave_type = nm_setting_connection_get_port_type(s_con);
                 if (!g_strcmp0(slave_type, NM_SETTING_MACSEC_SETTING_NAME))
-                    master = nm_setting_connection_get_master(s_con);
+                    master = nm_setting_connection_get_controller(s_con);
 
                 if (master && g_strcmp0(priv->parent, master) != 0) {
                     g_set_error(error,
diff --git a/src/libnm-core-impl/nm-setting-match.c b/src/libnm-core-impl/nm-setting-match.c
index 7736b7c0..255283fc 100644
--- a/src/libnm-core-impl/nm-setting-match.c
+++ b/src/libnm-core-impl/nm-setting-match.c
@@ -739,6 +739,7 @@ nm_setting_match_class_init(NMSettingMatchClass *klass)
                                             NM_SETTING_MATCH_INTERFACE_NAME,
                                             PROP_INTERFACE_NAME,
                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                            NULL,
                                             NMSettingMatch,
                                             interface_name);
 
@@ -764,6 +765,7 @@ nm_setting_match_class_init(NMSettingMatchClass *klass)
                                             NM_SETTING_MATCH_KERNEL_COMMAND_LINE,
                                             PROP_KERNEL_COMMAND_LINE,
                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                            NULL,
                                             NMSettingMatch,
                                             kernel_command_line);
 
@@ -783,6 +785,7 @@ nm_setting_match_class_init(NMSettingMatchClass *klass)
                                             NM_SETTING_MATCH_DRIVER,
                                             PROP_DRIVER,
                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                            NULL,
                                             NMSettingMatch,
                                             driver);
 
@@ -824,6 +827,7 @@ nm_setting_match_class_init(NMSettingMatchClass *klass)
                                             NM_SETTING_MATCH_PATH,
                                             PROP_PATH,
                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                            NULL,
                                             NMSettingMatch,
                                             path);
 
diff --git a/src/libnm-core-impl/nm-setting-ovs-bridge.c b/src/libnm-core-impl/nm-setting-ovs-bridge.c
index bc5dd04a..410d8771 100644
--- a/src/libnm-core-impl/nm-setting-ovs-bridge.c
+++ b/src/libnm-core-impl/nm-setting-ovs-bridge.c
@@ -153,7 +153,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
             return FALSE;
         }
 
-        if (nm_setting_connection_get_master(s_con)) {
+        if (nm_setting_connection_get_controller(s_con)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/src/libnm-core-impl/nm-setting-ovs-interface.c b/src/libnm-core-impl/nm-setting-ovs-interface.c
index 88379087..47c73665 100644
--- a/src/libnm-core-impl/nm-setting-ovs-interface.c
+++ b/src/libnm-core-impl/nm-setting-ovs-interface.c
@@ -308,7 +308,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
             return FALSE;
         }
 
-        if (!nm_setting_connection_get_master(s_con)) {
+        if (!nm_setting_connection_get_controller(s_con)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/src/libnm-core-impl/nm-setting-ovs-port.c b/src/libnm-core-impl/nm-setting-ovs-port.c
index f2071d43..107d76cd 100644
--- a/src/libnm-core-impl/nm-setting-ovs-port.c
+++ b/src/libnm-core-impl/nm-setting-ovs-port.c
@@ -413,7 +413,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
             return FALSE;
         }
 
-        if (!nm_setting_connection_get_master(s_con)) {
+        if (!nm_setting_connection_get_controller(s_con)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h
index 1276c903..a1ae6825 100644
--- a/src/libnm-core-impl/nm-setting-private.h
+++ b/src/libnm-core-impl/nm-setting-private.h
@@ -189,6 +189,7 @@ typedef struct {
     gint64      route_metric;
     int         auto_route_ext_gw;
     int         replace_local_rule;
+    int         dhcp_send_release;
     gint32      required_timeout;
     gint32      dad_timeout;
     gint32      dhcp_timeout;
@@ -421,6 +422,19 @@ _nm_setting_connection_autoconnect_ports_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_
 gboolean
 _nm_setting_connection_autoconnect_slaves_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
 
+gboolean _nm_setting_wireless_mac_denylist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *_nm_setting_wireless_mac_denylist_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+gboolean
+_nm_setting_wireless_mac_blacklist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_wired_mac_denylist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *_nm_setting_wired_mac_denylist_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_wired_mac_blacklist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
 GVariant *_nm_setting_to_dbus(NMSetting                              *setting,
                               NMConnection                           *connection,
                               NMConnectionSerializationFlags          flags,
@@ -741,7 +755,7 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
             !NM_FLAGS_ANY((param_flags),                                                          \
                           ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE)));       \
         G_STATIC_ASSERT((min_value) <= (default_value));                                          \
-        G_STATIC_ASSERT((default_value) == 0 || (default_value) -1u < (max_value));               \
+        G_STATIC_ASSERT((default_value) == 0 || (default_value) - 1u < (max_value));              \
         G_STATIC_ASSERT((max_value) <= G_MAXUINT64);                                              \
                                                                                                   \
         _param_spec = g_param_spec_uint64("" prop_name "",                                        \
@@ -868,39 +882,39 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
 
 /*****************************************************************************/
 
-#define _nm_setting_property_define_direct_strv(properties_override,                         \
-                                                obj_properties,                              \
-                                                prop_name,                                   \
-                                                prop_id,                                     \
-                                                param_flags,                                 \
-                                                private_struct_type,                         \
-                                                private_struct_field,                        \
-                                                ... /* extra NMSettInfoProperty fields */)   \
-    G_STMT_START                                                                             \
-    {                                                                                        \
-        GParamSpec *_param_spec;                                                             \
-                                                                                             \
-        G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags), ~(NM_SETTING_PARAM_FUZZY_IGNORE)));     \
-                                                                                             \
-        _param_spec = g_param_spec_boxed("" prop_name "",                                    \
-                                         "",                                                 \
-                                         "",                                                 \
-                                         G_TYPE_STRV,                                        \
-                                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY         \
-                                             | G_PARAM_STATIC_STRINGS | (param_flags));      \
-                                                                                             \
-        (obj_properties)[(prop_id)] = _param_spec;                                           \
-                                                                                             \
-        _nm_properties_override_gobj((properties_override),                                  \
-                                     _param_spec,                                            \
-                                     &nm_sett_info_propert_type_direct_strv,                 \
-                                     .direct_offset =                                        \
-                                         NM_STRUCT_OFFSET_ENSURE_TYPE(NMValueStrv,           \
-                                                                      private_struct_type,   \
-                                                                      private_struct_field), \
-                                     __VA_ARGS__);                                           \
-    }                                                                                        \
-    G_STMT_END
+#define _nm_setting_property_define_direct_strv(properties_override,                             \
+                                                obj_properties,                                  \
+                                                prop_name,                                       \
+                                                prop_id,                                         \
+                                                param_flags,                                     \
+                                                property_type,                                   \
+                                                private_struct_type,                             \
+                                                private_struct_field,                            \
+                                                ... /* extra NMSettInfoProperty fields */)       \
+    ({                                                                                           \
+        GParamSpec                  *_param_spec;                                                \
+        const NMSettInfoPropertType *_property_type;                                             \
+        G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags), ~(NM_SETTING_PARAM_FUZZY_IGNORE)));         \
+                                                                                                 \
+        _param_spec = g_param_spec_boxed("" prop_name "",                                        \
+                                         "",                                                     \
+                                         "",                                                     \
+                                         G_TYPE_STRV,                                            \
+                                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY             \
+                                             | G_PARAM_STATIC_STRINGS | (param_flags));          \
+                                                                                                 \
+        (obj_properties)[(prop_id)] = _param_spec;                                               \
+        _property_type              = (property_type) ?: &nm_sett_info_propert_type_direct_strv; \
+                                                                                                 \
+        _nm_properties_override_gobj((properties_override),                                      \
+                                     _param_spec,                                                \
+                                     _property_type,                                             \
+                                     .direct_offset =                                            \
+                                         NM_STRUCT_OFFSET_ENSURE_TYPE(NMValueStrv,               \
+                                                                      private_struct_type,       \
+                                                                      private_struct_field),     \
+                                     __VA_ARGS__);                                               \
+    })
 
 /*****************************************************************************/
 
diff --git a/src/libnm-core-impl/nm-setting-team.c b/src/libnm-core-impl/nm-setting-team.c
index 191ed9ae..08364af8 100644
--- a/src/libnm-core-impl/nm-setting-team.c
+++ b/src/libnm-core-impl/nm-setting-team.c
@@ -122,19 +122,13 @@ nm_team_link_watcher_new_ethtool(int delay_up, int delay_down, GError **error)
         return NULL;
     }
 
-    NM_PRAGMA_WARNING_DISABLE("-Warray-bounds")
-    NM_PRAGMA_WARNING_DISABLE("-Walloc-size")
-
-    watcher = g_malloc(nm_offsetofend(NMTeamLinkWatcher, ethtool));
+    watcher = g_malloc(sizeof(NMTeamLinkWatcher));
 
     watcher->ref_count          = 1;
     watcher->type               = LINK_WATCHER_ETHTOOL;
     watcher->ethtool.delay_up   = delay_up;
     watcher->ethtool.delay_down = delay_down;
 
-    NM_PRAGMA_WARNING_REENABLE
-    NM_PRAGMA_WARNING_REENABLE
-
     return watcher;
 }
 
diff --git a/src/libnm-core-impl/nm-setting-vlan.c b/src/libnm-core-impl/nm-setting-vlan.c
index 534ed713..16e3cf2a 100644
--- a/src/libnm-core-impl/nm-setting-vlan.c
+++ b/src/libnm-core-impl/nm-setting-vlan.c
@@ -616,7 +616,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
                 slave_type = nm_setting_connection_get_port_type(s_con);
                 if (!g_strcmp0(slave_type, NM_SETTING_VLAN_SETTING_NAME))
-                    master = nm_setting_connection_get_master(s_con);
+                    master = nm_setting_connection_get_controller(s_con);
 
                 if (master && g_strcmp0(priv->parent, master) != 0) {
                     g_set_error(error,
diff --git a/src/libnm-core-impl/nm-setting-wired.c b/src/libnm-core-impl/nm-setting-wired.c
index e09fd70d..2c8562d3 100644
--- a/src/libnm-core-impl/nm-setting-wired.c
+++ b/src/libnm-core-impl/nm-setting-wired.c
@@ -39,6 +39,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingWired,
                              PROP_CLONED_MAC_ADDRESS,
                              PROP_GENERATE_MAC_ADDRESS_MASK,
                              PROP_MAC_ADDRESS_BLACKLIST,
+                             PROP_MAC_ADDRESS_DENYLIST,
                              PROP_MTU,
                              PROP_S390_SUBCHANNELS,
                              PROP_S390_NETTYPE,
@@ -53,20 +54,20 @@ typedef struct {
         guint              len;
         guint              n_alloc;
     } s390_options;
-    GArray *mac_address_blacklist;
-    char  **s390_subchannels;
-    char   *port;
-    char   *duplex;
-    char   *device_mac_address;
-    char   *cloned_mac_address;
-    char   *generate_mac_address_mask;
-    char   *s390_nettype;
-    char   *wol_password;
-    int     accept_all_mac_addresses;
-    guint32 wake_on_lan;
-    guint32 speed;
-    guint32 mtu;
-    bool    auto_negotiate;
+    char      **s390_subchannels;
+    char       *port;
+    char       *duplex;
+    char       *device_mac_address;
+    char       *cloned_mac_address;
+    char       *generate_mac_address_mask;
+    char       *s390_nettype;
+    char       *wol_password;
+    NMValueStrv mac_address_denylist;
+    int         accept_all_mac_addresses;
+    guint32     wake_on_lan;
+    guint32     speed;
+    guint32     mtu;
+    bool        auto_negotiate;
 } NMSettingWiredPrivate;
 
 /**
@@ -277,166 +278,349 @@ nm_setting_wired_get_generate_mac_address_mask(NMSettingWired *setting)
 }
 
 /**
- * nm_setting_wired_get_mac_address_blacklist:
+ * nm_setting_wired_get_mac_address_denylist:
  * @setting: the #NMSettingWired
  *
- * Returns: the #NMSettingWired:mac-address-blacklist property of the setting
+ * Returns: the #NMSettingWired:mac-address-denylist property of the setting
+ * 
+ * Since: 1.48
  **/
 const char *const *
-nm_setting_wired_get_mac_address_blacklist(NMSettingWired *setting)
+nm_setting_wired_get_mac_address_denylist(NMSettingWired *setting)
 {
-    NMSettingWiredPrivate *priv;
-
     g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), NULL);
 
-    priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-    return nm_g_array_data(priv->mac_address_blacklist);
+    return nm_strvarray_get_strv_notnull(
+        NM_SETTING_WIRED_GET_PRIVATE(setting)->mac_address_denylist.arr,
+        NULL);
 }
 
 /**
- * nm_setting_wired_get_num_mac_blacklist_items:
+ * nm_setting_wired_get_num_mac_denylist_items:
  * @setting: the #NMSettingWired
  *
- * Returns: the number of blacklisted MAC addresses
+ * Returns: the number of denylisted MAC addresses
+ * 
+ * Since: 1.48
  **/
-guint32
-nm_setting_wired_get_num_mac_blacklist_items(NMSettingWired *setting)
+guint
+nm_setting_wired_get_num_mac_denylist_items(NMSettingWired *setting)
 {
     g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), 0);
 
-    return NM_SETTING_WIRED_GET_PRIVATE(setting)->mac_address_blacklist->len;
+    return nm_g_array_len(NM_SETTING_WIRED_GET_PRIVATE(setting)->mac_address_denylist.arr);
 }
 
 /**
- * nm_setting_wired_get_mac_blacklist_item:
+ * nm_setting_wired_get_mac_denylist_item:
  * @setting: the #NMSettingWired
  * @idx: the zero-based index of the MAC address entry
  *
- * Since 1.46, access at index "len" is allowed and returns NULL.
- *
- * Returns: the blacklisted MAC address string (hex-digits-and-colons notation)
+ * Returns: the denylisted MAC address string (hex-digits-and-colons notation)
  * at index @idx
+ * 
+ * Since: 1.48
  **/
 const char *
-nm_setting_wired_get_mac_blacklist_item(NMSettingWired *setting, guint32 idx)
+nm_setting_wired_get_mac_denylist_item(NMSettingWired *setting, guint idx)
 {
-    NMSettingWiredPrivate *priv;
-
     g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), NULL);
 
-    priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-
-    if (idx == priv->mac_address_blacklist->len) {
-        return NULL;
-    }
-
-    g_return_val_if_fail(idx < priv->mac_address_blacklist->len, NULL);
-
-    return nm_g_array_index(priv->mac_address_blacklist, const char *, idx);
+    return nm_strvarray_get_idxnull_or_greturn(
+        NM_SETTING_WIRED_GET_PRIVATE(setting)->mac_address_denylist.arr,
+        idx);
 }
 
 /**
- * nm_setting_wired_add_mac_blacklist_item:
+ * nm_setting_wired_add_mac_denylist_item:
  * @setting: the #NMSettingWired
- * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist
+ * @mac: the MAC address string (hex-digits-and-colons notation) to denylist
  *
- * Adds a new MAC address to the #NMSettingWired:mac-address-blacklist property.
+ * Adds a new MAC address to the #NMSettingWired:mac-address-denylist property.
  *
  * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address
  * is invalid or was already present
+ * 
+ * Since: 1.48
  **/
 gboolean
-nm_setting_wired_add_mac_blacklist_item(NMSettingWired *setting, const char *mac)
+nm_setting_wired_add_mac_denylist_item(NMSettingWired *setting, const char *mac)
 {
     NMSettingWiredPrivate *priv;
+    guint8                 mac_bin[ETH_ALEN];
     const char            *candidate;
-    int                    i;
+    guint                  i;
+    guint                  len;
 
     g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), FALSE);
     g_return_val_if_fail(mac != NULL, FALSE);
 
-    if (!nm_utils_hwaddr_valid(mac, ETH_ALEN))
+    if (!_nm_utils_hwaddr_aton_exact(mac, mac_bin, ETH_ALEN))
         return FALSE;
 
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-    for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
-        if (nm_utils_hwaddr_matches(mac, -1, candidate, -1))
+    len  = nm_g_array_len(priv->mac_address_denylist.arr);
+    for (i = 0; i < len; i++) {
+        candidate = nm_g_array_index(priv->mac_address_denylist.arr, char *, i);
+        if (nm_utils_hwaddr_matches(mac_bin, ETH_ALEN, candidate, -1))
             return FALSE;
     }
 
-    mac = nm_utils_hwaddr_canonical(mac, ETH_ALEN);
-    g_array_append_val(priv->mac_address_blacklist, mac);
-    _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
+    nm_g_array_append_simple(nm_strvarray_ensure(&priv->mac_address_denylist.arr),
+                             nm_utils_hwaddr_ntoa(mac_bin, ETH_ALEN));
+    _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
     return TRUE;
 }
 
 /**
- * nm_setting_wired_remove_mac_blacklist_item:
+ * nm_setting_wired_remove_mac_denylist_item:
  * @setting: the #NMSettingWired
  * @idx: index number of the MAC address
  *
- * Removes the MAC address at index @idx from the blacklist.
+ * Removes the MAC address at index @idx from the denylist.
+ * 
+ * Since: 1.48
  **/
 void
-nm_setting_wired_remove_mac_blacklist_item(NMSettingWired *setting, guint32 idx)
+nm_setting_wired_remove_mac_denylist_item(NMSettingWired *setting, guint idx)
 {
     NMSettingWiredPrivate *priv;
 
     g_return_if_fail(NM_IS_SETTING_WIRED(setting));
 
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-    g_return_if_fail(idx < priv->mac_address_blacklist->len);
+    if (!priv->mac_address_denylist.arr) {
+        return;
+    }
+
+    g_return_if_fail(idx < priv->mac_address_denylist.arr->len);
 
-    g_array_remove_index(priv->mac_address_blacklist, idx);
-    _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
+    g_array_remove_index(priv->mac_address_denylist.arr, idx);
+    _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
 }
 
 /**
- * nm_setting_wired_remove_mac_blacklist_item_by_value:
+ * nm_setting_wired_remove_mac_denylist_item_by_value:
  * @setting: the #NMSettingWired
  * @mac: the MAC address string (hex-digits-and-colons notation) to remove from
- * the blacklist
+ * the denylist
  *
- * Removes the MAC address @mac from the blacklist.
+ * Removes the MAC address @mac from the denylist.
  *
  * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not.
+ * 
+ * Since: 1.48
  **/
 gboolean
-nm_setting_wired_remove_mac_blacklist_item_by_value(NMSettingWired *setting, const char *mac)
+nm_setting_wired_remove_mac_denylist_item_by_value(NMSettingWired *setting, const char *mac)
 {
     NMSettingWiredPrivate *priv;
+    guint8                 mac_bin[ETH_ALEN];
     const char            *candidate;
-    int                    i;
+    guint                  i;
 
     g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), FALSE);
     g_return_val_if_fail(mac != NULL, FALSE);
 
+    if (!_nm_utils_hwaddr_aton_exact(mac, mac_bin, ETH_ALEN))
+        return FALSE;
+
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-    for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
-        if (!nm_utils_hwaddr_matches(mac, -1, candidate, -1)) {
-            g_array_remove_index(priv->mac_address_blacklist, i);
-            _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
-            return TRUE;
+    if (priv->mac_address_denylist.arr) {
+        for (i = 0; i < priv->mac_address_denylist.arr->len; i++) {
+            candidate = nm_g_array_index(priv->mac_address_denylist.arr, char *, i);
+            if (nm_utils_hwaddr_matches(mac_bin, ETH_ALEN, candidate, -1)) {
+                g_array_remove_index(priv->mac_address_denylist.arr, i);
+                _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
+                return TRUE;
+            }
         }
     }
+
     return FALSE;
 }
 
 /**
+ * nm_setting_wired_clear_mac_denylist_items:
+ * @setting: the #NMSettingWired
+ *
+ * Removes all denylisted MAC addresses.
+ * 
+ * Since: 1.48
+ **/
+void
+nm_setting_wired_clear_mac_denylist_items(NMSettingWired *setting)
+{
+    g_return_if_fail(NM_IS_SETTING_WIRED(setting));
+
+    if (nm_strvarray_clear(&NM_SETTING_WIRED_GET_PRIVATE(setting)->mac_address_denylist.arr))
+        _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
+}
+
+/**
+ * nm_setting_wired_get_mac_address_blacklist:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:mac-address-blacklist property of the setting
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_get_mac_address_denylist() instead.
+ **/
+const char *const *
+nm_setting_wired_get_mac_address_blacklist(NMSettingWired *setting)
+{
+    return nm_setting_wired_get_mac_address_denylist(setting);
+}
+
+/**
+ * nm_setting_wired_get_num_mac_blacklist_items:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the number of blacklisted MAC addresses
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_get_num_mac_denylist_items() instead.
+ **/
+guint32
+nm_setting_wired_get_num_mac_blacklist_items(NMSettingWired *setting)
+{
+    return nm_setting_wired_get_num_mac_denylist_items(setting);
+}
+
+/**
+ * nm_setting_wired_get_mac_blacklist_item:
+ * @setting: the #NMSettingWired
+ * @idx: the zero-based index of the MAC address entry
+ *
+ * Since 1.48, access at index "len" is allowed and returns NULL.
+ *
+ * Returns: the blacklisted MAC address string (hex-digits-and-colons notation)
+ * at index @idx
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_get_mac_denylist_item() instead.
+ **/
+const char *
+nm_setting_wired_get_mac_blacklist_item(NMSettingWired *setting, guint32 idx)
+{
+    return nm_setting_wired_get_mac_denylist_item(setting, idx);
+}
+
+/**
+ * nm_setting_wired_add_mac_blacklist_item:
+ * @setting: the #NMSettingWired
+ * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist
+ *
+ * Adds a new MAC address to the #NMSettingWired:mac-address-blacklist property.
+ *
+ * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address
+ * is invalid or was already present
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_add_mac_denylist_item() instead.
+ **/
+gboolean
+nm_setting_wired_add_mac_blacklist_item(NMSettingWired *setting, const char *mac)
+{
+    return nm_setting_wired_add_mac_denylist_item(setting, mac);
+}
+
+/**
+ * nm_setting_wired_remove_mac_blacklist_item:
+ * @setting: the #NMSettingWired
+ * @idx: index number of the MAC address
+ *
+ * Removes the MAC address at index @idx from the blacklist.
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_remove_mac_denylist_item() instead.
+ **/
+void
+nm_setting_wired_remove_mac_blacklist_item(NMSettingWired *setting, guint32 idx)
+{
+    return nm_setting_wired_remove_mac_denylist_item(setting, idx);
+}
+
+/**
+ * nm_setting_wired_remove_mac_blacklist_item_by_value:
+ * @setting: the #NMSettingWired
+ * @mac: the MAC address string (hex-digits-and-colons notation) to remove from
+ * the blacklist
+ *
+ * Removes the MAC address @mac from the blacklist.
+ *
+ * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not.
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_remove_mac_denylist_item_by_value() instead.
+ **/
+gboolean
+nm_setting_wired_remove_mac_blacklist_item_by_value(NMSettingWired *setting, const char *mac)
+{
+    return nm_setting_wired_remove_mac_denylist_item_by_value(setting, mac);
+}
+
+/**
  * nm_setting_wired_clear_mac_blacklist_items:
  * @setting: the #NMSettingWired
  *
  * Removes all blacklisted MAC addresses.
+ *
+ * Deprecated: 1.48. Use nm_setting_wired_clear_mac_denylist_items() instead.
  **/
 void
 nm_setting_wired_clear_mac_blacklist_items(NMSettingWired *setting)
 {
-    g_return_if_fail(NM_IS_SETTING_WIRED(setting));
+    return nm_setting_wired_clear_mac_denylist_items(setting);
+}
+
+gboolean
+_nm_setting_wired_mac_blacklist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    const gchar **mac_blacklist;
+
+    if (!_nm_setting_use_legacy_property(setting,
+                                         connection_dict,
+                                         NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST,
+                                         NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST)) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+    mac_blacklist = g_variant_get_strv(value, NULL);
+
+    g_object_set(setting, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, mac_blacklist, NULL);
+    return TRUE;
+}
+
+gboolean
+_nm_setting_wired_mac_denylist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    const gchar **mac_denylist;
+
+    if (!_nm_setting_use_legacy_property(setting,
+                                         connection_dict,
+                                         NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST,
+                                         NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST)) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+    mac_denylist = g_variant_get_strv(value, NULL);
+
+    g_object_set(setting, NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST, mac_denylist, NULL);
+    return TRUE;
+}
+
+GVariant *
+_nm_setting_wired_mac_denylist_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    const char *const *mac_denylist;
+    /* FIXME: `mac-address-denylist` is an alias of `mac-address-blacklist` property.
+     * Serializing the property to the clients would break them as they won't
+     * be able to drop it if they are not aware of the existance of
+     * `mac-address-denylist`. In order to give them time to adapt their code,
+     * NetworkManager is not serializing `mac-address-denylist` on DBus.
+     */
+    if (_nm_utils_is_manager_process) {
+        return NULL;
+    }
 
-    g_array_set_size(NM_SETTING_WIRED_GET_PRIVATE(setting)->mac_address_blacklist, 0);
-    _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
+    mac_denylist = nm_setting_wired_get_mac_address_denylist(NM_SETTING_WIRED(setting));
+
+    return g_variant_new_strv(mac_denylist, -1);
 }
 
 /**
@@ -815,20 +999,22 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i);
+    if (priv->mac_address_denylist.arr) {
+        for (i = 0; i < priv->mac_address_denylist.arr->len; i++) {
+            const char *mac = nm_g_array_index(priv->mac_address_denylist.arr, const char *, i);
 
-        if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
-            g_set_error(error,
-                        NM_CONNECTION_ERROR,
-                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                        _("'%s' is not a valid MAC address"),
-                        mac);
-            g_prefix_error(error,
-                           "%s.%s: ",
-                           NM_SETTING_WIRED_SETTING_NAME,
-                           NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
-            return FALSE;
+            if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("'%s' is not a valid MAC address"),
+                            mac);
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               NM_SETTING_WIRED_SETTING_NAME,
+                               NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST);
+                return FALSE;
+            }
         }
     }
 
@@ -993,14 +1179,6 @@ compare_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 /*****************************************************************************/
 
 static void
-clear_blacklist_item(char **item_p)
-{
-    g_free(*item_p);
-}
-
-/*****************************************************************************/
-
-static void
 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
     NMSettingWired        *setting = NM_SETTING_WIRED(object);
@@ -1012,9 +1190,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_CLONED_MAC_ADDRESS:
         g_value_set_string(value, nm_setting_wired_get_cloned_mac_address(setting));
         break;
-    case PROP_MAC_ADDRESS_BLACKLIST:
-        g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist));
-        break;
     case PROP_S390_SUBCHANNELS:
         g_value_set_boxed(value, priv->s390_subchannels);
         break;
@@ -1037,8 +1212,6 @@ static void
 set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
     NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE(object);
-    const char *const     *blacklist;
-    const char            *mac;
 
     switch (prop_id) {
     case PROP_CLONED_MAC_ADDRESS:
@@ -1046,18 +1219,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         priv->cloned_mac_address =
             _nm_utils_hwaddr_canonical_or_invalid(g_value_get_string(value), ETH_ALEN);
         break;
-    case PROP_MAC_ADDRESS_BLACKLIST:
-        blacklist = g_value_get_boxed(value);
-        g_array_set_size(priv->mac_address_blacklist, 0);
-        if (blacklist && *blacklist) {
-            guint i;
-
-            for (i = 0; blacklist[i]; i++) {
-                mac = _nm_utils_hwaddr_canonical_or_invalid(blacklist[i], ETH_ALEN);
-                g_array_append_val(priv->mac_address_blacklist, mac);
-            }
-        }
-        break;
     case PROP_S390_SUBCHANNELS:
         if (priv->s390_subchannels)
             g_strfreev(priv->s390_subchannels);
@@ -1135,13 +1296,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_wired_init(NMSettingWired *setting)
-{
-    NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-
-    /* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
-    priv->mac_address_blacklist = g_array_new(TRUE, FALSE, sizeof(char *));
-    g_array_set_clear_func(priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
-}
+{}
 
 /**
  * nm_setting_wired_new:
@@ -1164,7 +1319,6 @@ finalize(GObject *object)
     _s390_options_clear(priv);
 
     g_free(priv->cloned_mac_address);
-    g_array_unref(priv->mac_address_blacklist);
     g_strfreev(priv->s390_subchannels);
 
     G_OBJECT_CLASS(nm_setting_wired_parent_class)->finalize(object);
@@ -1176,6 +1330,7 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
     GObjectClass   *object_class        = G_OBJECT_CLASS(klass);
     NMSettingClass *setting_class       = NM_SETTING_CLASS(klass);
     GArray         *properties_override = _nm_sett_info_property_override_create_array();
+    guint           prop_idx;
 
     object_class->get_property = get_property;
     object_class->set_property = set_property;
@@ -1486,11 +1641,67 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      * example: HWADDR_BLACKLIST="00:22:68:11:69:08 00:11:22:11:44:55"
      * ---end---
      */
-    _nm_setting_property_define_gprop_strv_oldstyle(properties_override,
-                                                    obj_properties,
-                                                    NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST,
-                                                    PROP_MAC_ADDRESS_BLACKLIST,
-                                                    NM_SETTING_PARAM_FUZZY_IGNORE);
+    prop_idx = _nm_setting_property_define_direct_strv(
+        properties_override,
+        obj_properties,
+        NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST,
+        PROP_MAC_ADDRESS_BLACKLIST,
+        NM_SETTING_PARAM_FUZZY_IGNORE,
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY,
+                                       .direct_type = NM_VALUE_TYPE_STRV,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_direct,
+                                       .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
+                                       .from_dbus_fcn =
+                                           _nm_setting_wired_mac_blacklist_from_dbus, ),
+        NMSettingWiredPrivate,
+        mac_address_denylist,
+        .direct_set_strv_normalize_hwaddr = TRUE,
+        .direct_strv_not_null             = TRUE,
+        .direct_is_aliased_field          = TRUE,
+        .is_deprecated                    = TRUE);
+
+    /**
+     * NMSettingWired:mac-address-denylist:
+     *
+     * If specified, this connection will never apply to the Ethernet device
+     * whose permanent MAC address matches an address in the list.  Each MAC
+     * address is in the standard hex-digits-and-colons notation
+     * (00:11:22:33:44:55).
+     **/
+    /* ---keyfile---
+     * property: mac-address-denylist
+     * format: list of MACs (separated with semicolons)
+     * description: MAC address denylist.
+     * example: mac-address-denylist= 00:22:68:12:79:A6;00:22:68:12:79:78
+     * ---end---
+     */
+    /* ---ifcfg-rh---
+     * property: mac-address-denylist
+     * variable: HWADDR_BLACKLIST(+)
+     * description: It denies usage of the connection for any device whose address
+     *   is listed.
+     * example: HWADDR_BLACKLIST="00:22:68:11:69:08 00:11:22:11:44:55"
+     * ---end---
+     */
+    _nm_setting_property_define_direct_strv(
+        properties_override,
+        obj_properties,
+        NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST,
+        PROP_MAC_ADDRESS_DENYLIST,
+        NM_SETTING_PARAM_FUZZY_IGNORE,
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY,
+                                       .direct_type   = NM_VALUE_TYPE_STRV,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                       .to_dbus_fcn   = _nm_setting_wired_mac_denylist_to_dbus,
+                                       .from_dbus_fcn = _nm_setting_wired_mac_denylist_from_dbus, ),
+        NMSettingWiredPrivate,
+        mac_address_denylist,
+        .direct_set_strv_normalize_hwaddr = TRUE,
+        .direct_strv_not_null             = TRUE,
+        .direct_also_notify               = obj_properties[PROP_MAC_ADDRESS_BLACKLIST], );
+
+    nm_g_array_index(properties_override, NMSettInfoProperty, prop_idx).direct_also_notify =
+        obj_properties[PROP_MAC_ADDRESS_DENYLIST];
 
     /**
      * NMSettingWired:mtu:
diff --git a/src/libnm-core-impl/nm-setting-wireless.c b/src/libnm-core-impl/nm-setting-wireless.c
index 244dcdcc..207b47d8 100644
--- a/src/libnm-core-impl/nm-setting-wireless.c
+++ b/src/libnm-core-impl/nm-setting-wireless.c
@@ -37,6 +37,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingWireless,
                              PROP_CLONED_MAC_ADDRESS,
                              PROP_GENERATE_MAC_ADDRESS_MASK,
                              PROP_MAC_ADDRESS_BLACKLIST,
+                             PROP_MAC_ADDRESS_DENYLIST,
                              PROP_MTU,
                              PROP_SEEN_BSSIDS,
                              PROP_HIDDEN,
@@ -46,24 +47,24 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingWireless,
                              PROP_AP_ISOLATION, );
 
 typedef struct {
-    GBytes    *ssid;
-    GArray    *mac_address_blacklist;
-    GPtrArray *seen_bssids;
-    char      *mode;
-    char      *band;
-    char      *bssid;
-    char      *device_mac_address;
-    char      *cloned_mac_address;
-    char      *generate_mac_address_mask;
-    int        ap_isolation;
-    guint32    mac_address_randomization;
-    guint32    channel;
-    guint32    rate;
-    guint32    tx_power;
-    guint32    mtu;
-    guint32    powersave;
-    guint32    wake_on_wlan;
-    bool       hidden;
+    GBytes     *ssid;
+    GPtrArray  *seen_bssids;
+    char       *mode;
+    char       *band;
+    char       *bssid;
+    char       *device_mac_address;
+    char       *cloned_mac_address;
+    char       *generate_mac_address_mask;
+    NMValueStrv mac_address_denylist;
+    int         ap_isolation;
+    guint32     mac_address_randomization;
+    guint32     channel;
+    guint32     rate;
+    guint32     tx_power;
+    guint32     mtu;
+    guint32     powersave;
+    guint32     wake_on_wlan;
+    bool        hidden;
 } NMSettingWirelessPrivate;
 
 /**
@@ -462,166 +463,295 @@ nm_setting_wireless_get_generate_mac_address_mask(NMSettingWireless *setting)
 }
 
 /**
- * nm_setting_wireless_get_mac_address_blacklist:
+ * nm_setting_wireless_get_mac_address_denylist:
  * @setting: the #NMSettingWireless
  *
- * Returns: the #NMSettingWireless:mac-address-blacklist property of the setting
+ * Returns: the #NMSettingWireless:mac-address-denylist property of the setting
+ *
+ * Since: 1.48
  **/
 const char *const *
-nm_setting_wireless_get_mac_address_blacklist(NMSettingWireless *setting)
+nm_setting_wireless_get_mac_address_denylist(NMSettingWireless *setting)
 {
-    NMSettingWirelessPrivate *priv;
-
     g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), NULL);
 
-    priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-    return nm_g_array_data(priv->mac_address_blacklist);
+    return nm_strvarray_get_strv_notnull(
+        NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_denylist.arr,
+        NULL);
 }
 
 /**
- * nm_setting_wireless_get_num_mac_blacklist_items:
+ * nm_setting_wireless_get_num_mac_denylist_items:
  * @setting: the #NMSettingWireless
  *
- * Returns: the number of blacklisted MAC addresses
+ * Returns: the number of denylisted MAC addresses
+ *
+ * Since: 1.48
  **/
-guint32
-nm_setting_wireless_get_num_mac_blacklist_items(NMSettingWireless *setting)
+guint
+nm_setting_wireless_get_num_mac_denylist_items(NMSettingWireless *setting)
 {
     g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), 0);
 
-    return NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist->len;
+    return nm_g_array_len(NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_denylist.arr);
 }
 
 /**
- * nm_setting_wireless_get_mac_blacklist_item:
+ * nm_setting_wireless_get_mac_denylist_item:
  * @setting: the #NMSettingWireless
  * @idx: the zero-based index of the MAC address entry
  *
- * Since 1.46, access at index "len" is allowed and returns NULL.
- *
- * Returns: the blacklisted MAC address string (hex-digits-and-colons notation)
+ * Returns: the denylisted MAC address string (hex-digits-and-colons notation)
  * at index @idx
+ *
+ * Since: 1.48
  **/
 const char *
-nm_setting_wireless_get_mac_blacklist_item(NMSettingWireless *setting, guint32 idx)
+nm_setting_wireless_get_mac_denylist_item(NMSettingWireless *setting, guint32 idx)
 {
-    NMSettingWirelessPrivate *priv;
-
     g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), NULL);
 
-    priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-
-    if (idx == priv->mac_address_blacklist->len) {
-        return NULL;
-    }
-
-    g_return_val_if_fail(idx < priv->mac_address_blacklist->len, NULL);
-
-    return nm_g_array_index(priv->mac_address_blacklist, const char *, idx);
+    return nm_strvarray_get_idxnull_or_greturn(
+        NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_denylist.arr,
+        idx);
 }
 
 /**
- * nm_setting_wireless_add_mac_blacklist_item:
+ * nm_setting_wireless_add_mac_denylist_item:
  * @setting: the #NMSettingWireless
- * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist
+ * @mac: the MAC address string (hex-digits-and-colons notation) to denylist
  *
- * Adds a new MAC address to the #NMSettingWireless:mac-address-blacklist property.
+ * Adds a new MAC address to the #NMSettingWireless:mac-address-denylist property.
  *
  * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address
  * is invalid or was already present
+ *
+ * Since: 1.48
  **/
 gboolean
-nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const char *mac)
+nm_setting_wireless_add_mac_denylist_item(NMSettingWireless *setting, const char *mac)
 {
     NMSettingWirelessPrivate *priv;
+    guint8                    mac_bin[ETH_ALEN];
     const char               *candidate;
-    int                       i;
+    guint                     i;
+    guint                     len;
 
     g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), FALSE);
     g_return_val_if_fail(mac != NULL, FALSE);
 
-    if (!nm_utils_hwaddr_valid(mac, ETH_ALEN))
+    if (!_nm_utils_hwaddr_aton_exact(mac, mac_bin, ETH_ALEN))
         return FALSE;
 
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-    for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
-        if (nm_utils_hwaddr_matches(mac, -1, candidate, -1))
+    len  = nm_g_array_len(priv->mac_address_denylist.arr);
+    for (i = 0; i < len; i++) {
+        candidate = nm_g_array_index(priv->mac_address_denylist.arr, char *, i);
+        if (nm_utils_hwaddr_matches(mac_bin, ETH_ALEN, candidate, -1))
             return FALSE;
     }
 
-    mac = nm_utils_hwaddr_canonical(mac, ETH_ALEN);
-    g_array_append_val(priv->mac_address_blacklist, mac);
-    _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
+    nm_g_array_append_simple(nm_strvarray_ensure(&priv->mac_address_denylist.arr),
+                             nm_utils_hwaddr_ntoa(mac_bin, ETH_ALEN));
+    _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
     return TRUE;
 }
 
 /**
- * nm_setting_wireless_remove_mac_blacklist_item:
+ * nm_setting_wireless_remove_mac_denylist_item:
  * @setting: the #NMSettingWireless
  * @idx: index number of the MAC address
  *
- * Removes the MAC address at index @idx from the blacklist.
+ * Removes the MAC address at index @idx from the denylist.
+ *
+ * Since: 1.48
  **/
 void
-nm_setting_wireless_remove_mac_blacklist_item(NMSettingWireless *setting, guint32 idx)
+nm_setting_wireless_remove_mac_denylist_item(NMSettingWireless *setting, guint idx)
 {
     NMSettingWirelessPrivate *priv;
 
     g_return_if_fail(NM_IS_SETTING_WIRELESS(setting));
 
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-    g_return_if_fail(idx < priv->mac_address_blacklist->len);
+    if (!priv->mac_address_denylist.arr) {
+        return;
+    }
+
+    g_return_if_fail(idx < priv->mac_address_denylist.arr->len);
 
-    g_array_remove_index(priv->mac_address_blacklist, idx);
-    _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
+    g_array_remove_index(priv->mac_address_denylist.arr, idx);
+    _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
 }
 
 /**
- * nm_setting_wireless_remove_mac_blacklist_item_by_value:
+ * nm_setting_wireless_remove_mac_denylist_item_by_value:
  * @setting: the #NMSettingWireless
  * @mac: the MAC address string (hex-digits-and-colons notation) to remove from
- * the blacklist
+ * the denylist
  *
- * Removes the MAC address @mac from the blacklist.
+ * Removes the MAC address @mac from the denylist.
  *
  * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not.
+ *
+ * Since: 1.48
  **/
 gboolean
-nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *setting, const char *mac)
+nm_setting_wireless_remove_mac_denylist_item_by_value(NMSettingWireless *setting, const char *mac)
 {
     NMSettingWirelessPrivate *priv;
+    guint8                    mac_bin[ETH_ALEN];
     const char               *candidate;
-    int                       i;
+    guint                     i;
 
     g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), FALSE);
     g_return_val_if_fail(mac != NULL, FALSE);
 
+    if (!_nm_utils_hwaddr_aton_exact(mac, mac_bin, ETH_ALEN))
+        return FALSE;
+
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-    for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
-        if (nm_utils_hwaddr_matches(mac, -1, candidate, -1)) {
-            g_array_remove_index(priv->mac_address_blacklist, i);
-            _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
-            return TRUE;
+
+    if (priv->mac_address_denylist.arr) {
+        for (i = 0; i < priv->mac_address_denylist.arr->len; i++) {
+            candidate = nm_g_array_index(priv->mac_address_denylist.arr, char *, i);
+            if (nm_utils_hwaddr_matches(mac_bin, ETH_ALEN, candidate, -1)) {
+                g_array_remove_index(priv->mac_address_denylist.arr, i);
+                _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
+                return TRUE;
+            }
         }
     }
+
     return FALSE;
 }
 
 /**
- * nm_setting_wireless_clear_mac_blacklist_items:
+ * nm_setting_wireless_clear_mac_denylist_items:
  * @setting: the #NMSettingWireless
  *
- * Removes all blacklisted MAC addresses.
+ * Removes all denylisted MAC addresses.
+ *
+ * Since: 1.48
  **/
 void
-nm_setting_wireless_clear_mac_blacklist_items(NMSettingWireless *setting)
+nm_setting_wireless_clear_mac_denylist_items(NMSettingWireless *setting)
 {
     g_return_if_fail(NM_IS_SETTING_WIRELESS(setting));
 
-    g_array_set_size(NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_blacklist, 0);
-    _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
+    if (nm_strvarray_clear(&NM_SETTING_WIRELESS_GET_PRIVATE(setting)->mac_address_denylist.arr))
+        _notify(setting, PROP_MAC_ADDRESS_DENYLIST);
+}
+
+/**
+ * nm_setting_wireless_get_mac_address_blacklist:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:mac-address-blacklist property of the setting
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_get_mac_address_denylist() instead.
+ **/
+const char *const *
+nm_setting_wireless_get_mac_address_blacklist(NMSettingWireless *setting)
+{
+    return nm_setting_wireless_get_mac_address_denylist(setting);
+}
+
+/**
+ * nm_setting_wireless_get_num_mac_blacklist_items:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the number of blacklist MAC addresses
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_get_num_mac_denylist_items() instead.
+ **/
+guint32
+nm_setting_wireless_get_num_mac_blacklist_items(NMSettingWireless *setting)
+{
+    return nm_setting_wireless_get_num_mac_denylist_items(setting);
+}
+
+/**
+ * nm_setting_wireless_get_mac_blacklist_item:
+ * @setting: the #NMSettingWireless
+ * @idx: the zero-based index of the MAC address entry
+ *
+ * Since 1.46, access at index "len" is allowed and returns NULL.
+ *
+ * Returns: the denylisted MAC address string (hex-digits-and-colons notation)
+ * at index @idx
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_get_mac_denylist_item() instead.
+ **/
+const char *
+nm_setting_wireless_get_mac_blacklist_item(NMSettingWireless *setting, guint32 idx)
+{
+    return nm_setting_wireless_get_mac_denylist_item(setting, idx);
+}
+
+/**
+ * nm_setting_wireless_add_mac_blacklist_item:
+ * @setting: the #NMSettingWireless
+ * @mac: the MAC address string (hex-digits-and-colons notation) to denylist
+ *
+ * Adds a new MAC address to the #NMSettingWireless:mac-address-denylist property.
+ *
+ * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address
+ * is invalid or was already present
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_add_mac_denylist_item() instead.
+ **/
+gboolean
+nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const char *mac)
+{
+    return nm_setting_wireless_add_mac_denylist_item(setting, mac);
+}
+
+/**
+ * nm_setting_wireless_remove_mac_blacklist_item:
+ * @setting: the #NMSettingWireless
+ * @idx: index number of the MAC address
+ *
+ * Removes the MAC address at index @idx from the denylist.
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_remove_mac_denylist_item() instead.
+ **/
+void
+nm_setting_wireless_remove_mac_blacklist_item(NMSettingWireless *setting, guint32 idx)
+{
+    return nm_setting_wireless_remove_mac_denylist_item(setting, idx);
+}
+
+/**
+ * nm_setting_wireless_remove_mac_blacklist_item_by_value:
+ * @setting: the #NMSettingWireless
+ * @mac: the MAC address string (hex-digits-and-colons notation) to remove from
+ * the denylist
+ *
+ * Removes the MAC address @mac from the denylist.
+ *
+ * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not.
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_remove_mac_denylist_item_by_value() instead.
+ **/
+gboolean
+nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *setting, const char *mac)
+{
+    return nm_setting_wireless_remove_mac_denylist_item_by_value(setting, mac);
+}
+
+/**
+ * nm_setting_wireless_clear_mac_blacklist_items:
+ * @setting: the #NMSettingWireless
+ *
+ * Removes all denylisted MAC addresses.
+ *
+ * Deprecated: 1.48. Use nm_setting_wireless_clear_mac_denylist_items() instead.
+ **/
+void
+nm_setting_wireless_clear_mac_blacklist_items(NMSettingWireless *setting)
+{
+    return nm_setting_wireless_clear_mac_denylist_items(setting);
 }
 
 /**
@@ -783,6 +913,61 @@ _to_dbus_fcn_seen_bssids(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
     return NULL;
 }
 
+gboolean
+_nm_setting_wireless_mac_blacklist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    const gchar **mac_blacklist;
+
+    if (!_nm_setting_use_legacy_property(setting,
+                                         connection_dict,
+                                         NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
+                                         NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST)) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+    mac_blacklist = g_variant_get_strv(value, NULL);
+
+    g_object_set(setting, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, mac_blacklist, NULL);
+    return TRUE;
+}
+
+gboolean
+_nm_setting_wireless_mac_denylist_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    const gchar **mac_blacklist;
+
+    if (!_nm_setting_use_legacy_property(setting,
+                                         connection_dict,
+                                         NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
+                                         NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST)) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+    mac_blacklist = g_variant_get_strv(value, NULL);
+
+    g_object_set(setting, NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST, mac_blacklist, NULL);
+    return TRUE;
+}
+
+GVariant *
+_nm_setting_wireless_mac_denylist_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    const char *const *mac_denylist;
+    /* FIXME: `mac-address-denylist` is an alias of `mac-address-blacklist` property.
+     * Serializing the property to the clients would break them as they won't
+     * be able to drop it if they are not aware of the existance of
+     * `mac-address-denylist`. In order to give them time to adapt their code,
+     * NetworkManager is not serializing `mac-address-denylist` on DBus.
+     */
+    if (_nm_utils_is_manager_process) {
+        return NULL;
+    }
+
+    mac_denylist = nm_setting_wireless_get_mac_address_denylist(NM_SETTING_WIRELESS(setting));
+
+    return g_variant_new_strv(mac_denylist, -1);
+}
+
 static gboolean
 _from_dbus_fcn_seen_bssids(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
@@ -1041,20 +1226,22 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i);
+    if (priv->mac_address_denylist.arr) {
+        for (i = 0; i < priv->mac_address_denylist.arr->len; i++) {
+            const char *mac = nm_g_array_index(priv->mac_address_denylist.arr, const char *, i);
 
-        if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
-            g_set_error(error,
-                        NM_CONNECTION_ERROR,
-                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                        _("'%s' is not a valid MAC address"),
-                        mac);
-            g_prefix_error(error,
-                           "%s.%s: ",
-                           NM_SETTING_WIRELESS_SETTING_NAME,
-                           NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
-            return FALSE;
+            if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("'%s' is not a valid MAC address"),
+                            mac);
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               NM_SETTING_WIRELESS_SETTING_NAME,
+                               NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST);
+                return FALSE;
+            }
         }
     }
 
@@ -1216,12 +1403,6 @@ nm_setting_wireless_get_wake_on_wlan(NMSettingWireless *setting)
     return NM_SETTING_WIRELESS_GET_PRIVATE(setting)->wake_on_wlan;
 }
 
-static void
-clear_blacklist_item(char **item_p)
-{
-    g_free(*item_p);
-}
-
 /*****************************************************************************/
 
 static void
@@ -1234,9 +1415,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_CLONED_MAC_ADDRESS:
         g_value_set_string(value, nm_setting_wireless_get_cloned_mac_address(setting));
         break;
-    case PROP_MAC_ADDRESS_BLACKLIST:
-        g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist));
-        break;
     case PROP_SEEN_BSSIDS:
         g_value_take_boxed(
             value,
@@ -1255,8 +1433,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 {
     NMSettingWireless        *self = NM_SETTING_WIRELESS(object);
     NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE(self);
-    const char *const        *blacklist;
-    const char               *mac;
     gboolean                  bool_val;
     _PropertyEnums            prop1 = PROP_0;
     _PropertyEnums            prop2 = PROP_0;
@@ -1281,18 +1457,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
         nm_gobject_notify_together(self, prop1, prop2);
         break;
-    case PROP_MAC_ADDRESS_BLACKLIST:
-        blacklist = g_value_get_boxed(value);
-        g_array_set_size(priv->mac_address_blacklist, 0);
-        if (blacklist && blacklist[0]) {
-            gsize i;
-
-            for (i = 0; blacklist[i]; i++) {
-                mac = _nm_utils_hwaddr_canonical_or_invalid(blacklist[i], ETH_ALEN);
-                g_array_append_val(priv->mac_address_blacklist, mac);
-            }
-        }
-        break;
     case PROP_SEEN_BSSIDS:
     {
         gs_unref_ptrarray GPtrArray *arr_old = NULL;
@@ -1321,13 +1485,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_wireless_init(NMSettingWireless *setting)
-{
-    NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-
-    /* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
-    priv->mac_address_blacklist = g_array_new(TRUE, FALSE, sizeof(char *));
-    g_array_set_clear_func(priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
-}
+{}
 
 /**
  * nm_setting_wireless_new:
@@ -1348,7 +1506,6 @@ finalize(GObject *object)
     NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE(object);
 
     g_free(priv->cloned_mac_address);
-    g_array_unref(priv->mac_address_blacklist);
     nm_g_ptr_array_unref(priv->seen_bssids);
 
     G_OBJECT_CLASS(nm_setting_wireless_parent_class)->finalize(object);
@@ -1360,6 +1517,7 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
     GObjectClass   *object_class        = G_OBJECT_CLASS(klass);
     NMSettingClass *setting_class       = NM_SETTING_CLASS(klass);
     GArray         *properties_override = _nm_sett_info_property_override_create_array_sized(25);
+    guint           prop_idx;
 
     object_class->set_property = set_property;
     object_class->get_property = get_property;
@@ -1733,11 +1891,66 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      *   is listed.
      * ---end---
      */
-    _nm_setting_property_define_gprop_strv_oldstyle(properties_override,
-                                                    obj_properties,
-                                                    NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
-                                                    PROP_MAC_ADDRESS_BLACKLIST,
-                                                    NM_SETTING_PARAM_FUZZY_IGNORE);
+    prop_idx = _nm_setting_property_define_direct_strv(
+        properties_override,
+        obj_properties,
+        NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
+        PROP_MAC_ADDRESS_BLACKLIST,
+        NM_SETTING_PARAM_FUZZY_IGNORE,
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY,
+                                       .direct_type = NM_VALUE_TYPE_STRV,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_direct,
+                                       .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
+                                       .from_dbus_fcn =
+                                           _nm_setting_wireless_mac_blacklist_from_dbus, ),
+        NMSettingWirelessPrivate,
+        mac_address_denylist,
+        .direct_set_strv_normalize_hwaddr = TRUE,
+        .direct_strv_not_null             = TRUE,
+        .direct_is_aliased_field          = TRUE,
+        .is_deprecated                    = TRUE);
+
+    /**
+     * NMSettingWireless:mac-address-denylist:
+     *
+     * A list of permanent MAC addresses of Wi-Fi devices to which this
+     * connection should never apply.  Each MAC address should be given in the
+     * standard hex-digits-and-colons notation (eg "00:11:22:33:44:55").
+     **/
+    /* ---keyfile---
+     * property: mac-address-denylist
+     * format: list of MACs (separated with semicolons)
+     * description: MAC address denylist.
+     * example: mac-address-denylist= 00:22:68:12:79:A6;00:22:68:12:79:78
+     * ---end---
+     */
+    /* ---ifcfg-rh---
+     * property: mac-address-denylist
+     * variable: HWADDR_BLACKLIST(+)
+     * description: It denies usage of the connection for any device whose address
+     *   is listed.
+     * ---end---
+     */
+    _nm_setting_property_define_direct_strv(
+        properties_override,
+        obj_properties,
+        NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST,
+        PROP_MAC_ADDRESS_DENYLIST,
+        NM_SETTING_PARAM_FUZZY_IGNORE,
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY,
+                                       .direct_type = NM_VALUE_TYPE_STRV,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_direct,
+                                       .to_dbus_fcn = _nm_setting_wireless_mac_denylist_to_dbus,
+                                       .from_dbus_fcn =
+                                           _nm_setting_wireless_mac_denylist_from_dbus, ),
+        NMSettingWirelessPrivate,
+        mac_address_denylist,
+        .direct_set_strv_normalize_hwaddr = TRUE,
+        .direct_strv_not_null             = TRUE,
+        .direct_also_notify               = obj_properties[PROP_MAC_ADDRESS_BLACKLIST], );
+
+    nm_g_array_index(properties_override, NMSettInfoProperty, prop_idx).direct_also_notify =
+        obj_properties[PROP_MAC_ADDRESS_DENYLIST];
 
     /**
      * NMSettingWireless:seen-bssids:
diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c
index 8bc7b4bf..bbaa6fcd 100644
--- a/src/libnm-core-impl/nm-setting.c
+++ b/src/libnm-core-impl/nm-setting.c
@@ -8,6 +8,8 @@
 
 #include "nm-setting.h"
 
+#include <linux/if_ether.h>
+
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-glib-aux/nm-ref-string.h"
 #include "libnm-glib-aux/nm-secret-utils.h"
@@ -739,10 +741,29 @@ _property_direct_set_strv(const NMSettInfoSetting  *sett_info,
     if (!property_info->direct_strv_preserve_empty && strv && !strv[0])
         strv = NULL;
 
-    if (nm_strvarray_equal_strv(p_val->arr, strv, -1))
-        return FALSE;
+    if (property_info->direct_set_strv_normalize_hwaddr) {
+        gs_unref_array GArray *arr = NULL;
+        if (strv) {
+            nm_strvarray_ensure(&arr);
+
+            for (; strv[0]; strv++) {
+                nm_strvarray_add_take(arr,
+                                      _nm_utils_hwaddr_canonical_or_invalid(strv[0], ETH_ALEN));
+            }
+        }
+
+        if (nm_strvarray_equal(p_val->arr, arr))
+            return FALSE;
 
+        NM_SWAP(&p_val->arr, &arr);
+        return TRUE;
+    }
+
+    if (nm_strvarray_equal_strv(p_val->arr, strv, -1)) {
+        return FALSE;
+    }
     nm_strvarray_set_strv_full(&p_val->arr, strv, property_info->direct_strv_preserve_empty);
+
     return TRUE;
 }
 
@@ -844,7 +865,7 @@ _nm_setting_property_get_property_direct(GObject    *object,
             value,
             nm_strvarray_get_strv_full_dup(p_val->arr,
                                            NULL,
-                                           FALSE,
+                                           property_info->direct_strv_not_null,
                                            property_info->direct_strv_preserve_empty));
         return;
     }
@@ -2711,7 +2732,9 @@ _nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_
                         _nm_setting_property_to_dbus_fcn_direct_mac_address,
                         _nm_setting_connection_controller_to_dbus,
                         _nm_setting_connection_port_type_to_dbus,
-                        _nm_setting_connection_autoconnect_ports_to_dbus));
+                        _nm_setting_connection_autoconnect_ports_to_dbus,
+                        _nm_setting_wireless_mac_denylist_to_dbus,
+                        _nm_setting_wired_mac_denylist_to_dbus));
 
     if (!property_info->param_spec)
         return nm_assert_unreachable_val(NM_TERNARY_DEFAULT);
diff --git a/src/libnm-core-impl/nm-team-utils.c b/src/libnm-core-impl/nm-team-utils.c
index 6f2f5dd2..83452569 100644
--- a/src/libnm-core-impl/nm-team-utils.c
+++ b/src/libnm-core-impl/nm-team-utils.c
@@ -172,17 +172,17 @@ static const TeamAttrData team_attr_datas[] = {
 
 #define _VAL_INT32_RANGE(_default, _min, _max) \
     _VAL_INT32(_default), .has_range = TRUE,   \
-                          .range.r_int32 = {   \
-                              .min = _min,     \
-                              .max = _max,     \
+        .range.r_int32 = {                     \
+            .min = _min,                       \
+            .max = _max,                       \
     }
 
 #define _VAL_STRING() .default_val.v_string = NULL
 
-#define _VAL_STRING_RANGE(_valid_names)               \
-    _VAL_STRING(), .has_range = TRUE,                 \
-                   .range.r_string = {                \
-                       .valid_names = (_valid_names), \
+#define _VAL_STRING_RANGE(_valid_names)    \
+    _VAL_STRING(), .has_range = TRUE,      \
+        .range.r_string = {                \
+            .valid_names = (_valid_names), \
     }
 
 #define _VAL_UNSPEC() .default_val.v_string = (NULL)
@@ -1957,23 +1957,23 @@ _js_parse_locate_keys(const NMJsonVt *vt,
 
     nm_assert(vt);
 
-#define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content)  \
-    ({                                                                                             \
-        const TeamAttrData *_attr_data;                                                            \
-        gboolean            _handled = FALSE;                                                      \
-                                                                                                   \
-        (_keys)[(_level) -1] = (_cur_key);                                                         \
-        _attr_data           = _attr_data_find_by_json_key((_self)->d.is_port, (_keys), (_level)); \
-        if (_attr_data && _attr_data->js_keys_len == (_level)) {                                   \
-            if ((_found_keys)[_attr_data->team_attr])                                              \
-                *(_out_unrecognized_content) = TRUE;                                               \
-            (_found_keys)[_attr_data->team_attr] = (_cur_val);                                     \
-            _handled                             = TRUE;                                           \
-        } else if (!_attr_data || !nm_json_is_object((_cur_val))) {                                \
-            *(_out_unrecognized_content) = TRUE;                                                   \
-            _handled                     = TRUE;                                                   \
-        }                                                                                          \
-        _handled;                                                                                  \
+#define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content)   \
+    ({                                                                                              \
+        const TeamAttrData *_attr_data;                                                             \
+        gboolean            _handled = FALSE;                                                       \
+                                                                                                    \
+        (_keys)[(_level) - 1] = (_cur_key);                                                         \
+        _attr_data            = _attr_data_find_by_json_key((_self)->d.is_port, (_keys), (_level)); \
+        if (_attr_data && _attr_data->js_keys_len == (_level)) {                                    \
+            if ((_found_keys)[_attr_data->team_attr])                                               \
+                *(_out_unrecognized_content) = TRUE;                                                \
+            (_found_keys)[_attr_data->team_attr] = (_cur_val);                                      \
+            _handled                             = TRUE;                                            \
+        } else if (!_attr_data || !nm_json_is_object((_cur_val))) {                                 \
+            *(_out_unrecognized_content) = TRUE;                                                    \
+            _handled                     = TRUE;                                                    \
+        }                                                                                           \
+        _handled;                                                                                   \
     })
 
     nm_json_object_foreach (vt, root_js_obj, cur_key1, cur_val1) {
@@ -2809,16 +2809,8 @@ NMTeamSetting *
 nm_team_setting_new(gboolean is_port, const char *js_str)
 {
     NMTeamSetting *self;
-    gsize          l;
 
-    G_STATIC_ASSERT_EXPR(sizeof(*self) == sizeof(self->_data_priv));
-    G_STATIC_ASSERT_EXPR(
-        sizeof(*self)
-        == NM_MAX(nm_offsetofend(NMTeamSetting, d.master), nm_offsetofend(NMTeamSetting, d.port)));
-
-    l = is_port ? nm_offsetofend(NMTeamSetting, d.port) : nm_offsetofend(NMTeamSetting, d.master);
-
-    self = g_malloc0(l);
+    self = g_malloc0(sizeof(NMTeamSetting));
 
     self->_data_priv.is_port                 = is_port;
     self->_data_priv.strict_validated        = TRUE;
diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c
index 761f74bd..b6e24141 100644
--- a/src/libnm-core-impl/nm-utils.c
+++ b/src/libnm-core-impl/nm-utils.c
@@ -506,7 +506,8 @@ nmtst_system_encodings_get(void)
 
 /*****************************************************************************/
 
-static void __attribute__((constructor)) _nm_utils_init(void)
+static void __attribute__((constructor))
+_nm_utils_init(void)
 {
     static int initialized = 0;
 
diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c
index bf0f272c..f745d059 100644
--- a/src/libnm-core-impl/tests/test-general.c
+++ b/src/libnm-core-impl/tests/test-general.c
@@ -3962,7 +3962,7 @@ typedef struct {
 
 typedef struct {
     const char *name;
-    DiffKey     keys[32];
+    DiffKey     keys[33];
 } DiffSetting;
 
 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
@@ -4036,6 +4036,7 @@ test_connection_diff_a_only(void)
           {NM_SETTING_CONNECTION_MUD_URL, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY, NM_SETTING_DIFF_RESULT_IN_A},
+          {NM_SETTING_CONNECTION_DOWN_ON_POWEROFF, NM_SETTING_DIFF_RESULT_IN_A},
           {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}}},
         {NM_SETTING_WIRED_SETTING_NAME,
          {
@@ -4047,6 +4048,7 @@ test_connection_diff_a_only(void)
              {NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_WIRED_S390_NETTYPE, NM_SETTING_DIFF_RESULT_IN_A},
@@ -4088,6 +4090,7 @@ test_connection_diff_a_only(void)
              {NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE, NM_SETTING_DIFF_RESULT_IN_A},
              {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
          }},
     };
diff --git a/src/libnm-core-impl/tests/test-keyfile.c b/src/libnm-core-impl/tests/test-keyfile.c
index 2b21e583..9f546971 100644
--- a/src/libnm-core-impl/tests/test-keyfile.c
+++ b/src/libnm-core-impl/tests/test-keyfile.c
@@ -200,7 +200,7 @@ _nm_keyfile_read(GKeyFile            *keyfile,
              * keyfile reader does not add that (unless a [proxy] section
              * is present. */
             s_con = nm_connection_get_setting_connection(con);
-            if (s_con && !nm_setting_connection_get_master(s_con)
+            if (s_con && !nm_setting_connection_get_controller(s_con)
                 && !nm_connection_get_setting_proxy(con))
                 nm_connection_add_setting(con, nm_setting_proxy_new());
         }
diff --git a/src/libnm-core-impl/tests/test-secrets.c b/src/libnm-core-impl/tests/test-secrets.c
index 2fa3f105..cb5c23a4 100644
--- a/src/libnm-core-impl/tests/test-secrets.c
+++ b/src/libnm-core-impl/tests/test-secrets.c
@@ -250,6 +250,8 @@ make_tls_phase2_connection(const char *detail, NMSetting8021xCKScheme scheme)
                                           &error);
     nmtst_assert_success(success, error);
 
+    g_object_set(s_8021x, NM_SETTING_802_1X_OPENSSL_CIPHERS, "DEFAULT@SECLEVEL=0", NULL);
+
     /* IP4 setting */
     s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new();
     nm_connection_add_setting(connection, NM_SETTING(s_ip4));
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index 4b5a0b6f..f3309166 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -4732,8 +4732,10 @@ test_setting_metadata(void)
                 g_assert(sip->param_spec->value_type == G_TYPE_BYTES);
             } else if (sip->property_type->direct_type == NM_VALUE_TYPE_STRV) {
                 g_assert(g_variant_type_equal(sip->property_type->dbus_type, "as"));
-                g_assert(sip->property_type->to_dbus_fcn
-                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(NM_IN_SET(sip->property_type->to_dbus_fcn,
+                                   _nm_setting_property_to_dbus_fcn_direct,
+                                   _nm_setting_wireless_mac_denylist_to_dbus,
+                                   _nm_setting_wired_mac_denylist_to_dbus));
                 g_assert(sip->param_spec);
                 g_assert(sip->param_spec->value_type == G_TYPE_STRV);
             } else
diff --git a/src/libnm-core-intern/nm-core-internal.h b/src/libnm-core-intern/nm-core-internal.h
index 3903467d..fc157c58 100644
--- a/src/libnm-core-intern/nm-core-internal.h
+++ b/src/libnm-core-intern/nm-core-internal.h
@@ -821,6 +821,10 @@ struct _NMSettInfoProperty {
      * normalize the string via g_ascii_strdown(). */
     bool direct_set_string_ascii_strdown : 1;
 
+    /* If TRUE, this is a NM_VALUE_TYPE_STRV direct property holding MAC addresses,
+     * and the setter will normalize them via _nm_utils_hwaddr_canonical_or_invalid(). */
+    bool direct_set_strv_normalize_hwaddr : 1;
+
     /* If TRUE, this is a NM_VALUE_TYPE_STRING direct property, and the setter will
      * normalize the string via g_strstrip(). */
     bool direct_set_string_strip : 1;
@@ -870,6 +874,10 @@ struct _NMSettInfoProperty {
      * an empty array. */
     bool direct_strv_preserve_empty : 1;
 
+    /* This flag indicates that an empty strv array should be returned
+     * instead of NULL if it hadn't been created yet. */
+    bool direct_strv_not_null : 1;
+
     /* Usually, properties that are set to the default value for the GParamSpec
      * are not serialized to GVariant (and NULL is returned by to_dbus_data().
      * Set this flag to force always converting the property even if the value
diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h
index 66cd590d..cf6e8090 100644
--- a/src/libnm-core-public/nm-dbus-interface.h
+++ b/src/libnm-core-public/nm-dbus-interface.h
@@ -612,6 +612,25 @@ typedef enum {
  * @NM_DEVICE_STATE_REASON_PEER_NOT_FOUND: The Wi-Fi P2P peer could not be found
  * @NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED: The device handler dispatcher returned an
  *   error. Since: 1.46
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT: The device is unmanaged because the device type
+ *   is unmanaged by default. Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN: The device is unmanaged because it is an
+ *   external device and is unconfigured (down or without addresses). Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT: The device is unmanaged because the link is
+ *   not initialized by udev. Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING: The device is unmanaged because NetworkManager is
+ *   quitting. Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING: The device is unmanaged because networking is
+ *   disabled or the system is suspended. Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF: The device is unmanaged by user decision in
+ *   NetworkManager.conf ('unmanaged' in a [device*] section). Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT: The device is unmanaged by explicit user
+ *   decision (e.g. 'nmcli device set $DEV managed no'). Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS: The device is unmanaged by user decision
+ *   via settings plugin ('unmanaged-devices' for keyfile or 'NM_CONTROLLED=no' for ifcfg-rh).
+ *   Since: 1.48
+ * @NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV: The device is unmanaged via udev rule. Since: 1.48
+
  *
  * Device state change reason codes
  */
@@ -685,6 +704,15 @@ typedef enum {
     NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED     = 66,
     NM_DEVICE_STATE_REASON_PEER_NOT_FOUND                 = 67,
     NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED          = 68,
+    NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT           = 69,
+    NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN        = 70,
+    NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT        = 71,
+    NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING             = 72,
+    NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING             = 73,
+    NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF            = 74,
+    NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT        = 75,
+    NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS        = 76,
+    NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV            = 77,
 } NMDeviceStateReason;
 
 /**
@@ -991,6 +1019,11 @@ typedef enum {
  *   With this flag, the rollback detaches all external ports.
  *   This only has an effect for bridge ports. Before 1.38, this was the default
  *   behavior. Since: 1.38.
+ * @NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS: during rollback,
+ *   by default changes to global DNS via D-BUS interface are preserved.
+ *   With this flag, the rollback reverts the global DNS changes made via D-Bus
+ *   interface. Global DNS defined in [global-dns] section of
+ *   NetworkManager.conf is not impacted by this flag. Since: 1.48.
  *
  * The flags for CheckpointCreate call
  *
@@ -1003,6 +1036,7 @@ typedef enum /*< flags >*/ {
     NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES     = 0x04,
     NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING          = 0x08,
     NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS = 0x10,
+    NM_CHECKPOINT_CREATE_FLAG_TRACK_INTERNAL_GLOBAL_DNS  = 0x20,
 } NMCheckpointCreateFlags;
 
 /**
diff --git a/src/libnm-core-public/nm-dbus-types.xml b/src/libnm-core-public/nm-dbus-types.xml
deleted file mode 100644
index d294453d..00000000
--- a/src/libnm-core-public/nm-dbus-types.xml
+++ /dev/null
@@ -1,2361 +0,0 @@
-<?xml version='1.0'?>
-<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?>
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
-<refentry id="nm-dbus-types">
-  <refmeta>
-    <refentrytitle role="top_of_page" id="nm-dbus-types.top_of_page">NetworkManager D-Bus API Types</refentrytitle>
-    <manvolnum>3</manvolnum>
-    <refmiscinfo>NetworkManager D-Bus API Types</refmiscinfo>
-  </refmeta>
-  <refnamediv>
-    <refname>NetworkManager D-Bus API Types</refname>
-    <refpurpose></refpurpose>
-  </refnamediv>
-
-  <refsect2 id="NMVersionInfoCapability" role="enum">
-    <title>enum NMVersionInfoCapability</title>
-    <indexterm zone="NMVersionInfoCapability">
-      <primary>NMVersionInfoCapability</primary>
-    </indexterm>
-    <para><para>%_NM_VERSION_INFO_CAPABILITY_UNUSED: a dummy capability. It has no meaning, don't use it.</para><para>Currently no enum values are defined. These capabilities are exposed on D-Bus in the "VersionInfo" bit field.</para><para>Since: 1.42</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMCapability" role="enum">
-    <title>enum NMCapability</title>
-    <indexterm zone="NMCapability">
-      <primary>NMCapability</primary>
-    </indexterm>
-    <para><para><link linkend="NMCapability">NMCapability</link> names the numbers in the Capabilities property. Capabilities are positive numbers. They are part of stable API and a certain capability number is guaranteed not to change.</para><para>The range 0x7000 - 0x7FFF of capabilities is guaranteed not to be used by upstream NetworkManager. It could thus be used for downstream extensions.</para><para>Since: 1.6</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CAPABILITY_TEAM</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Teams can be managed. This means the team device plugin is loaded.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CAPABILITY_OVS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>OpenVSwitch can be managed. This means the OVS device plugin is loaded. Since: 1.24.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMState" role="enum">
-    <title>enum NMState</title>
-    <indexterm zone="NMState">
-      <primary>NMState</primary>
-    </indexterm>
-    <para><para><link linkend="NMState">NMState</link> values indicate the current overall networking state.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Networking state is unknown. This indicates a daemon error that makes it unable to reasonably assess the state. In such event the applications are expected to assume Internet connectivity might be present and not disable controls that require network access. The graphical shells may hide the network accessibility indicator altogether since no meaningful status indication can be provided.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_ASLEEP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Networking is not enabled, the system is being suspended or resumed from suspend.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>There is no active network connection. The graphical shell should indicate  no network connectivity and the applications should not attempt to access the network.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_DISCONNECTING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>30</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Network connections are being cleaned up. The applications should tear down their network sessions.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_CONNECTING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>40</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A network connection is being started The graphical shell should indicate the network is being connected while the applications should still make no attempts to connect the network.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_CONNECTED_LOCAL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>50</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>There is only local IPv4 and/or IPv6 connectivity, but no default route to access the Internet. The graphical shell should indicate no network connectivity.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_CONNECTED_SITE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>60</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>There is only site-wide IPv4 and/or IPv6 connectivity. This means a default route is available, but the Internet connectivity check (see "Connectivity" property) did not succeed. The graphical shell should indicate limited network connectivity.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_STATE_CONNECTED_GLOBAL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>70</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>There is global IPv4 and/or IPv6 Internet connectivity This means the Internet connectivity check succeeded, the graphical shell should indicate full network connectivity.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMConnectivityState" role="enum">
-    <title>enum NMConnectivityState</title>
-    <indexterm zone="NMConnectivityState">
-      <primary>NMConnectivityState</primary>
-    </indexterm>
-    <para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTIVITY_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Network connectivity is unknown. This means the connectivity checks are disabled (e.g. on server installations) or has not run yet. The graphical shell should assume the Internet connection might be available and not present a captive portal window.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTIVITY_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The host is not connected to any network. There's no active connection that contains a default route to the internet and thus it makes no sense to even attempt a connectivity check. The graphical shell should use this state to indicate the network connection is unavailable.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTIVITY_PORTAL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The Internet connection is hijacked by a captive portal gateway. The graphical shell may open a sandboxed web browser window (because the captive portals typically attempt a man-in-the-middle attacks against the https connections) for the purpose of authenticating to a gateway and retrigger the connectivity check with CheckConnectivity() when the browser window is dismissed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTIVITY_LIMITED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The host is connected to a network, does not appear to be able to reach the full Internet, but a captive portal has not been detected.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTIVITY_FULL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The host is connected to a network, and appears to be able to reach the full Internet.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceType" role="enum">
-    <title>enum NMDeviceType</title>
-    <indexterm zone="NMDeviceType">
-      <primary>NMDeviceType</primary>
-    </indexterm>
-    <para><para><link linkend="NMDeviceType">NMDeviceType</link> values indicate the type of hardware represented by a device object.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>unknown device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_GENERIC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>14</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>generic support for unrecognized device types</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_ETHERNET</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a wired ethernet device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIFI</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an 802.11 Wi-Fi device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_UNUSED1</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>not used</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_UNUSED2</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>not used</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_BT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a Bluetooth device supporting PAN or DUN access protocols</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OLPC_MESH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an OLPC XO mesh networking device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIMAX</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an 802.16e Mobile WiMAX broadband device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_MODEM</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a modem supporting analog telephone, CDMA/EVDO, GSM/UMTS, or LTE network access protocols</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_INFINIBAND</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an IP-over-InfiniBand device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_BOND</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a bond master interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_VLAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an 802.1Q VLAN interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_ADSL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>12</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>ADSL modem</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_BRIDGE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>13</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a bridge master interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_TEAM</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>15</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a team master interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_TUN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>16</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a TUN or TAP interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_IP_TUNNEL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>17</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a IP tunnel interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_MACVLAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>18</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a MACVLAN interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_VXLAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>19</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a VXLAN interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_VETH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a VETH interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_MACSEC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>21</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a MACsec interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_DUMMY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>22</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a dummy interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_PPP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>23</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a PPP interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OVS_INTERFACE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>24</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a Open vSwitch interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OVS_PORT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>25</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a Open vSwitch port</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_OVS_BRIDGE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>26</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a Open vSwitch bridge</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WPAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>27</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a IEEE 802.15.4 (WPAN) MAC Layer Device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_6LOWPAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>28</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>6LoWPAN interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIREGUARD</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>29</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a WireGuard interface</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIFI_P2P</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>30</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an 802.11 Wi-Fi P2P device. Since: 1.16.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_VRF</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>31</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A VRF (Virtual Routing and Forwarding) interface. Since: 1.24.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_LOOPBACK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>32</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a loopback interface. Since: 1.42.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_HSR</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>33</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A HSR/PRP device. Since: 1.46.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceCapabilities" role="enum">
-    <title>enum NMDeviceCapabilities</title>
-    <indexterm zone="NMDeviceCapabilities">
-      <primary>NMDeviceCapabilities</primary>
-    </indexterm>
-    <para><para>General device capability flags.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_CAP_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device has no special capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_CAP_NM_SUPPORTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>NetworkManager supports this device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_CAP_CARRIER_DETECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>this device can indicate carrier status</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_CAP_IS_SOFTWARE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000004</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>this device is a software device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_CAP_SRIOV</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000008</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>this device supports single-root I/O virtualization</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceWifiCapabilities" role="enum">
-    <title>enum NMDeviceWifiCapabilities</title>
-    <indexterm zone="NMDeviceWifiCapabilities">
-      <primary>NMDeviceWifiCapabilities</primary>
-    </indexterm>
-    <para><para>802.11 specific device encryption and authentication capabilities.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device has no encryption/authentication capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_CIPHER_WEP40</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports 40/64-bit WEP encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_CIPHER_WEP104</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports 104/128-bit WEP encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_CIPHER_TKIP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000004</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports TKIP encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_CIPHER_CCMP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000008</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports AES/CCMP encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_WPA</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000010</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports WPA1 authentication</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_RSN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000020</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports WPA2/RSN authentication</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_AP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000040</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports Access Point mode</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_ADHOC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000080</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports Ad-Hoc mode</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_FREQ_VALID</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000100</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device reports frequency capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_FREQ_2GHZ</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000200</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports 2.4GHz frequencies</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_FREQ_5GHZ</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000400</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports 5GHz frequencies</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_FREQ_6GHZ</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000800</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports 6GHz frequencies. Since: 1.46.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_MESH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00001000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports acting as a mesh point. Since: 1.20.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIFI_DEVICE_CAP_IBSS_RSN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00002000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device supports WPA2/RSN in an IBSS network. Since: 1.22.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NM80211ApFlags" role="enum">
-    <title>enum NM80211ApFlags</title>
-    <indexterm zone="NM80211ApFlags">
-      <primary>NM80211ApFlags</primary>
-    </indexterm>
-    <para><para>802.11 access point flags.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>access point has no special capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_PRIVACY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>access point requires authentication and encryption (usually means WEP)</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_WPS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>access point supports some WPS method</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_WPS_PBC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000004</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>access point supports push-button WPS</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_FLAGS_WPS_PIN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000008</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>access point supports PIN-based WPS</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NM80211ApSecurityFlags" role="enum">
-    <title>enum NM80211ApSecurityFlags</title>
-    <indexterm zone="NM80211ApSecurityFlags">
-      <primary>NM80211ApSecurityFlags</primary>
-    </indexterm>
-    <para><para>802.11 access point security and authentication flags.  These flags describe the current security requirements of an access point as determined from the access point's beacon.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the access point has no special security requirements</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_PAIR_WEP40</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>40/64-bit WEP is supported for pairwise/unicast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_PAIR_WEP104</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>104/128-bit WEP is supported for pairwise/unicast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_PAIR_TKIP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000004</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>TKIP is supported for pairwise/unicast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_PAIR_CCMP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000008</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>AES/CCMP is supported for pairwise/unicast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_GROUP_WEP40</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000010</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>40/64-bit WEP is supported for group/broadcast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_GROUP_WEP104</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000020</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>104/128-bit WEP is supported for group/broadcast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_GROUP_TKIP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000040</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>TKIP is supported for group/broadcast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_GROUP_CCMP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000080</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>AES/CCMP is supported for group/broadcast encryption</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_PSK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000100</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>WPA/RSN Pre-Shared Key encryption is supported</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_802_1X</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000200</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>802.1x authentication and key management is supported</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_SAE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000400</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>WPA/RSN Simultaneous Authentication of Equals is supported</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_OWE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000800</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>WPA/RSN Opportunistic Wireless Encryption is supported</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_OWE_TM</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00001000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>WPA/RSN Opportunistic Wireless Encryption transition mode is supported. Since: 1.26.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00002000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>WPA3 Enterprise Suite-B 192 bit mode is supported. Since: 1.30.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NM80211Mode" role="enum">
-    <title>enum NM80211Mode</title>
-    <indexterm zone="NM80211Mode">
-      <primary>NM80211Mode</primary>
-    </indexterm>
-    <para><para>Indicates the 802.11 mode an access point or device is currently in.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_MODE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device or access point mode is unknown</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_MODE_ADHOC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>for both devices and access point objects, indicates the object is part of an Ad-Hoc 802.11 network without a central coordinating access point.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_MODE_INFRA</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device or access point is in infrastructure mode. For devices, this indicates the device is an 802.11 client/station.  For access point objects, this indicates the object is an access point that provides connectivity to clients.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_MODE_AP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is an access point/hotspot.  Not valid for access point objects; used only for hotspot mode on the local machine.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_802_11_MODE_MESH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is a 802.11s mesh point. Since: 1.20.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMBluetoothCapabilities" role="enum">
-    <title>enum NMBluetoothCapabilities</title>
-    <indexterm zone="NMBluetoothCapabilities">
-      <primary>NMBluetoothCapabilities</primary>
-    </indexterm>
-    <para><para><link linkend="NMBluetoothCapabilities">NMBluetoothCapabilities</link> values indicate the usable capabilities of a Bluetooth device.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_BT_CAPABILITY_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device has no usable capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_BT_CAPABILITY_DUN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device provides Dial-Up Networking capability</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_BT_CAPABILITY_NAP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>device provides Network Access Point capability</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceModemCapabilities" role="enum">
-    <title>enum NMDeviceModemCapabilities</title>
-    <indexterm zone="NMDeviceModemCapabilities">
-      <primary>NMDeviceModemCapabilities</primary>
-    </indexterm>
-    <para><para><link linkend="NMDeviceModemCapabilities">NMDeviceModemCapabilities</link> values indicate the generic radio access technology families a modem device supports.  For more information on the specific access technologies the device supports use the ModemManager D-Bus API.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_MODEM_CAPABILITY_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modem has no usable capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_MODEM_CAPABILITY_POTS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000001</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modem uses the analog wired telephone network and is not a wireless/cellular device</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000002</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modem supports at least one of CDMA 1xRTT, EVDO revision 0, EVDO revision A, or EVDO revision B</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000004</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modem supports at least one of GSM, GPRS, EDGE, UMTS, HSDPA, HSUPA, or HSPA+ packet switched data capability</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_MODEM_CAPABILITY_LTE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000008</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modem has LTE data capability</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_MODEM_CAPABILITY_5GNR</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x00000040</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modem has 5GNR data capability. Since: 1.36.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMWimaxNspNetworkType" role="enum">
-    <title>enum NMWimaxNspNetworkType</title>
-    <indexterm zone="NMWimaxNspNetworkType">
-      <primary>NMWimaxNspNetworkType</primary>
-    </indexterm>
-    <para><para>WiMAX network type.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>unknown network type</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIMAX_NSP_NETWORK_TYPE_HOME</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>home network</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIMAX_NSP_NETWORK_TYPE_PARTNER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>partner network</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>roaming partner network</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceState" role="enum">
-    <title>enum NMDeviceState</title>
-    <indexterm zone="NMDeviceState">
-      <primary>NMDeviceState</primary>
-    </indexterm>
-    <para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device's state is unknown</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_UNMANAGED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is recognized, but not managed by NetworkManager</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_UNAVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is managed by NetworkManager, but is not available for use.  Reasons may include the wireless switched off, missing firmware, no ethernet carrier, missing supplicant or modem manager, etc.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>30</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device can be activated, but is currently idle and not connected to a network.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_PREPARE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>40</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is preparing the connection to the network.  This may include operations like changing the MAC address, setting physical link properties, and anything else required to connect to the requested network.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_CONFIG</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>50</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is connecting to the requested network. This may include operations like associating with the Wi-Fi AP, dialing the modem, connecting to the remote Bluetooth device, etc.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_NEED_AUTH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>60</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device requires more information to continue connecting to the requested network.  This includes secrets like WiFi passphrases, login passwords, PIN codes, etc.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_IP_CONFIG</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>70</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is requesting IPv4 and/or IPv6 addresses and routing information from the network.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_IP_CHECK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>80</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is checking whether further action is required for the requested network connection.  This may include checking whether only local network access is available, whether a captive portal is blocking access to the Internet, etc.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_SECONDARIES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>90</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is waiting for a secondary connection (like a VPN) which must activated before the device can be activated</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_ACTIVATED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>100</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device has a network connection, either local or global.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_DEACTIVATING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>110</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a disconnection from the current network connection was requested, and the device is cleaning up resources used for that connection.  The network connection may still be valid.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>120</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device failed to connect to the requested network and is cleaning up the connection request</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceStateReason" role="enum">
-    <title>enum NMDeviceStateReason</title>
-    <indexterm zone="NMDeviceStateReason">
-      <primary>NMDeviceStateReason</primary>
-    </indexterm>
-    <para><para>Device state change reason codes</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>No reason given</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Unknown error</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_NOW_MANAGED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Device is now managed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_NOW_UNMANAGED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Device is now unmanaged</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_CONFIG_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The device could not be readied for configuration</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IP configuration could not be reserved (no available address, timeout, etc)</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The IP config is no longer valid</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_NO_SECRETS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Secrets were required, but not provided</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>802.1x supplicant disconnected</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>802.1x supplicant configuration failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>802.1x supplicant failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>802.1x supplicant took too long to authenticate</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PPP_START_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>12</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>PPP service failed to start</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PPP_DISCONNECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>13</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>PPP service disconnected</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PPP_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>14</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>PPP failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_DHCP_START_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>15</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>DHCP client failed to start</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_DHCP_ERROR</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>16</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>DHCP client error</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_DHCP_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>17</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>DHCP client failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SHARED_START_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>18</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Shared connection service failed to start</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SHARED_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>19</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Shared connection service failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>AutoIP service failed to start</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_AUTOIP_ERROR</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>21</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>AutoIP service error</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_AUTOIP_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>22</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>AutoIP service failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_BUSY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>23</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The line is busy</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>24</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>No dial tone</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>25</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>No carrier could be established</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>26</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The dialing request timed out</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>27</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The dialing attempt failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>28</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Modem initialization failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_APN_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>29</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Failed to select the specified APN</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>30</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Not searching for networks</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>31</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Network registration denied</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>32</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Network registration timed out</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>33</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Failed to register with the requested network</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>34</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>PIN check failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_FIRMWARE_MISSING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>35</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Necessary firmware for the device may be missing</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_REMOVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>36</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The device was removed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SLEEPING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>37</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>NetworkManager went to sleep</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_CONNECTION_REMOVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>38</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The device's active connection disappeared</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_USER_REQUESTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>39</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Device disconnected by user or client</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_CARRIER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>40</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Carrier/link changed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>41</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The device's existing connection was assumed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>42</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The supplicant is now available</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>43</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The modem could not be found</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_BT_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>44</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The Bluetooth connection failed or timed out</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>45</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>GSM Modem's SIM Card not inserted</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>46</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>GSM Modem's SIM Pin required</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>47</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>GSM Modem's SIM Puk required</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_GSM_SIM_WRONG</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>48</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>GSM Modem's SIM wrong</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_INFINIBAND_MODE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>49</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>InfiniBand device does not support connected mode</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>50</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A dependency of the connection failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_BR2684_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>51</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Problem with the RFC 2684 Ethernet over ADSL bridge</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>52</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>ModemManager not running</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SSID_NOT_FOUND</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>53</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The Wi-Fi network could not be found</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>54</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A secondary connection of the base connection failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>55</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>DCB or FCoE setup failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>56</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>teamd control failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>57</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Modem failed or no longer available</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_MODEM_AVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>58</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Modem now ready and available</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>59</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>SIM PIN was incorrect</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_NEW_ACTIVATION</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>60</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>New connection activation was enqueued</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PARENT_CHANGED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>61</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device's parent changed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>62</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device parent's management changed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_OVSDB_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>63</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>problem communicating with Open vSwitch database</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>64</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a duplicate IP address was detected</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>65</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The selected IP method is not supported</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>66</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>configuration of SR-IOV parameters failed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PEER_NOT_FOUND</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>67</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The Wi-Fi P2P peer could not be found</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>68</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The device handler dispatcher returned an error. Since: 1.46</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMMetered" role="enum">
-    <title>enum NMMetered</title>
-    <indexterm zone="NMMetered">
-      <primary>NMMetered</primary>
-    </indexterm>
-    <para><para>The NMMetered enum has two different purposes: one is to configure "connection.metered" setting of a connection profile in <link linkend="NMSettingConnection,">NMSettingConnection,</link> and the other is to express the actual metered state of the <link linkend="NMDevice">NMDevice</link> at a given moment.</para><para>For the connection profile only <link linkend="NM_METERED_UNKNOWN,">NM_METERED_UNKNOWN,</link> <link linkend="NM_METERED_NO">NM_METERED_NO</link> and <link linkend="NM_METERED_YES">NM_METERED_YES</link> are allowed.</para><para>The device's metered state at runtime is determined by the profile which is currently active. If the profile explicitly specifies <link linkend="NM_METERED_NO">NM_METERED_NO</link> or <link linkend="NM_METERED_YES,">NM_METERED_YES,</link> then the device's metered state is as such. If the connection profile leaves it undecided at <link linkend="NM_METERED_UNKNOWN">NM_METERED_UNKNOWN</link> (the default), then NetworkManager tries to guess the metered state, for example based on the device type or on DHCP options (like Android devices exposing a "ANDROID_METERED" DHCP vendor option). This then leads to either <link linkend="NM_METERED_GUESS_NO">NM_METERED_GUESS_NO</link> or <link linkend="NM_METERED_GUESS_YES">NM_METERED_GUESS_YES</link>.</para><para>Most applications probably should treat the runtime state <link linkend="NM_METERED_GUESS_YES">NM_METERED_GUESS_YES</link> like <link linkend="NM_METERED_YES,">NM_METERED_YES,</link> and all other states as not metered.</para><para>Note that the per-device metered states are then combined to a global metered state. This is basically the metered state of the device with the best default route. However, that generalization of a global metered state may not be correct if the default routes for IPv4 and IPv6 are on different devices, or if policy routing is configured. In general, the global metered state tries to express whether the traffic is likely metered, but since that depends on the traffic itself, there is not one answer in all cases. Hence, an application may want to consider the per-device's metered states.</para><para>Since: 1.2</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_METERED_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The metered status is unknown</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_METERED_YES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Metered, the value was explicitly configured</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_METERED_NO</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Not metered, the value was explicitly configured</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_METERED_GUESS_YES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Metered, the value was guessed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_METERED_GUESS_NO</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Not metered, the value was guessed</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMConnectionMultiConnect" role="enum">
-    <title>enum NMConnectionMultiConnect</title>
-    <indexterm zone="NMConnectionMultiConnect">
-      <primary>NMConnectionMultiConnect</primary>
-    </indexterm>
-    <para><para>Since: 1.14</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_DEFAULT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>indicates that the per-connection setting is unspecified. In this case, it will fallback to the default value, which is %NM_CONNECTION_MULTI_CONNECT_SINGLE.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_SINGLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the connection profile can only be active once at each moment. Activating a profile that is already active, will first deactivate it.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the profile can be manually activated multiple times on different devices. However, regarding autoconnect, the profile will autoconnect only if it is currently not connected otherwise.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CONNECTION_MULTI_CONNECT_MULTIPLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the profile can autoactivate and be manually activated multiple times together.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMActiveConnectionState" role="enum">
-    <title>enum NMActiveConnectionState</title>
-    <indexterm zone="NMActiveConnectionState">
-      <primary>NMActiveConnectionState</primary>
-    </indexterm>
-    <para><para><link linkend="NMActiveConnectionState">NMActiveConnectionState</link> values indicate the state of a connection to a specific network while it is starting, connected, or disconnecting from that network.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the state of the connection is unknown</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_ACTIVATING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>a network connection is being prepared</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_ACTIVATED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>there is a connection to the network</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_DEACTIVATING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the network connection is being torn down and cleaned up</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_DEACTIVATED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the network connection is disconnected and will be removed</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMActiveConnectionStateReason" role="enum">
-    <title>enum NMActiveConnectionStateReason</title>
-    <indexterm zone="NMActiveConnectionStateReason">
-      <primary>NMActiveConnectionStateReason</primary>
-    </indexterm>
-    <para><para>Active connection state reasons.</para><para>Since: 1.8</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The reason for the active connection state change is unknown.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>No reason was given for the active connection state change.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The active connection changed state because the user disconnected it.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The active connection changed state because the device it was using was disconnected.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The service providing the VPN connection was stopped.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The IP config of the active connection was invalid.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The connection attempt to the VPN service timed out.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A timeout occurred while starting the service providing the VPN connection.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Starting the service providing the VPN connection failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Necessary secrets for the connection were not provided.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_LOGIN_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Authentication to the server failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The connection was deleted from settings.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>12</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Master connection of this connection failed to activate.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>13</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Could not create the software device link.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>14</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The device this connection depended on disappeared.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMSecretAgentGetSecretsFlags" role="enum">
-    <title>enum NMSecretAgentGetSecretsFlags</title>
-    <indexterm zone="NMSecretAgentGetSecretsFlags">
-      <primary>NMSecretAgentGetSecretsFlags</primary>
-    </indexterm>
-    <para><para><link linkend="NMSecretAgentGetSecretsFlags">NMSecretAgentGetSecretsFlags</link> values modify the behavior of a GetSecrets request.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>no special behavior; by default no user interaction is allowed and requests for secrets are fulfilled from persistent storage, or if no secrets are available an error is returned.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>allows the request to interact with the user, possibly prompting via UI for secrets if any are required, or if none are found in persistent storage.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>explicitly prompt for new secrets from the user.  This flag signals that NetworkManager thinks any existing secrets are invalid or wrong.  This flag implies that interaction is allowed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>set if the request was initiated by user-requested action via the D-Bus interface, as opposed to automatically initiated by NetworkManager in response to (for example) scan results or carrier changes.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>indicates that WPS enrollment is active with PBC method. The agent may suggest that the user pushes a button on the router instead of supplying a PSK.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x80000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Internal flag, not part of the D-Bus API.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x40000000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Internal flag, not part of the D-Bus API.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMSecretAgentCapabilities" role="enum">
-    <title>enum NMSecretAgentCapabilities</title>
-    <indexterm zone="NMSecretAgentCapabilities">
-      <primary>NMSecretAgentCapabilities</primary>
-    </indexterm>
-    <para><para><link linkend="NMSecretAgentCapabilities">NMSecretAgentCapabilities</link> indicate various capabilities of the agent.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_CAPABILITY_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the agent supports no special capabilities</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SECRET_AGENT_CAPABILITY_VPN_HINTS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the agent supports passing hints to VPN plugin authentication dialogs.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMIPTunnelMode" role="enum">
-    <title>enum NMIPTunnelMode</title>
-    <indexterm zone="NMIPTunnelMode">
-      <primary>NMIPTunnelMode</primary>
-    </indexterm>
-    <para><para>The tunneling mode.</para><para>Since: 1.2</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Unknown/unset tunnel mode</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_IPIP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IP in IP tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_GRE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>GRE tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_SIT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>SIT tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_ISATAP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>ISATAP tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_VTI</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>VTI tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_IP6IP6</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv6 in IPv6 tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_IPIP6</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv4 in IPv6 tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_IP6GRE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv6 GRE tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_VTI6</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv6 VTI tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_GRETAP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>GRETAP tunnel</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_IP_TUNNEL_MODE_IP6GRETAP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv6 GRETAP tunnel</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMCheckpointCreateFlags" role="enum">
-    <title>enum NMCheckpointCreateFlags</title>
-    <indexterm zone="NMCheckpointCreateFlags">
-      <primary>NMCheckpointCreateFlags</primary>
-    </indexterm>
-    <para><para>The flags for CheckpointCreate call</para><para>Since: 1.12: Public since 1.4, g-ir since 1.12.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>no flags</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x01</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>when creating a new checkpoint, destroy all existing ones.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x02</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>upon rollback, delete any new connection added after the checkpoint. Since: 1.6.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x04</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>upon rollback, disconnect any new device appeared after the checkpoint. Since: 1.6.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x08</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>by default, creating a checkpoint fails if there are already existing checkpoints that reference the same devices. With this flag, creation of such checkpoints is allowed, however, if an older checkpoint that references overlapping devices gets rolled back, it will automatically destroy this checkpoint during rollback. This allows to create several overlapping checkpoints in parallel, and rollback to them at will. With the special case that rolling back to an older checkpoint will invalidate all overlapping younger checkpoints. This opts-in that the checkpoint can be automatically destroyed by the rollback of an older checkpoint. Since: 1.12.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_NO_PRESERVE_EXTERNAL_PORTS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>during rollback, by default externally added ports attached to bridge devices are preserved. With this flag, the rollback detaches all external ports. This only has an effect for bridge ports. Before 1.38, this was the default behavior. Since: 1.38.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMRollbackResult" role="enum">
-    <title>enum NMRollbackResult</title>
-    <indexterm zone="NMRollbackResult">
-      <primary>NMRollbackResult</primary>
-    </indexterm>
-    <para><para>The result of a checkpoint Rollback() operation for a specific device.</para><para>Since: 1.4</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_OK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the rollback succeeded.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_ERR_NO_DEVICE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device no longer exists.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is now unmanaged.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_ERR_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>other errors during rollback.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMSettingsConnectionFlags" role="enum">
-    <title>enum NMSettingsConnectionFlags</title>
-    <indexterm zone="NMSettingsConnectionFlags">
-      <primary>NMSettingsConnectionFlags</primary>
-    </indexterm>
-    <para><para>Flags describing the current activation state.</para><para>Since: 1.12</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_UNSAVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x01</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the connection is not saved to disk. That either means, that the connection is in-memory only and currently is not backed by a file. Or, that the connection is backed by a file, but has modifications in-memory that were not persisted to disk.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x02</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A connection is "nm-generated" if it was generated by NetworkManger. If the connection gets modified or saved by the user, the flag gets cleared. A nm-generated is also unsaved and has no backing file as it is in-memory only.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_VOLATILE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x04</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The connection will be deleted when it disconnects. That is for in-memory connections (unsaved), which are currently active but deleted on disconnect. Volatile connections are always unsaved, but they are also no backing file on disk and are entirely in-memory only.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_EXTERNAL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x08</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the profile was generated to represent an external configuration of a networking device. Since: 1.26.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMActivationStateFlags" role="enum">
-    <title>enum NMActivationStateFlags</title>
-    <indexterm zone="NMActivationStateFlags">
-      <primary>NMActivationStateFlags</primary>
-    </indexterm>
-    <para><para>Flags describing the current activation state.</para><para>Since: 1.10</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IS_MASTER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is a master.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IS_SLAVE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is a slave.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_LAYER2_READY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>layer2 is activated and ready.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IP4_READY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv4 setting is completed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IP6_READY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>IPv6 setting is completed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The master has any slave devices attached. This only makes sense if the device is a master.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x40</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the lifetime of the activation is bound to the visibility of the connection profile, which in turn depends on "connection.permissions" and whether a session for the user exists. Since: 1.16.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_EXTERNAL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x80</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the active connection was generated to represent an external configuration of a networking device. Since: 1.26.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMSettingsAddConnection2Flags" role="enum">
-    <title>enum NMSettingsAddConnection2Flags</title>
-    <indexterm zone="NMSettingsAddConnection2Flags">
-      <primary>NMSettingsAddConnection2Flags</primary>
-    </indexterm>
-    <para><para>Numeric flags for the "flags" argument of AddConnection2() D-Bus API.</para><para>Since: 1.20</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_ADD_CONNECTION2_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>to persist the connection to disk.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>to make the connection in-memory only.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>usually, when the connection has autoconnect enabled and gets added, it becomes eligible to autoconnect right away. Setting this flag, disables autoconnect until the connection is manually activated.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMSettingsUpdate2Flags" role="enum">
-    <title>enum NMSettingsUpdate2Flags</title>
-    <indexterm zone="NMSettingsUpdate2Flags">
-      <primary>NMSettingsUpdate2Flags</primary>
-    </indexterm>
-    <para><para>Since: 1.12</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_TO_DISK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>to persist the connection to disk.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>makes the profile in-memory. Note that such profiles are stored in keyfile format under /run. If the file is already in-memory, the file in /run is updated in-place. Otherwise, the previous storage for the profile is left unchanged on disk, and the in-memory copy shadows it. Note that the original filename of the previous persistent storage (if any) is remembered. That means, when later persisting the profile again to disk, the file on disk will be overwritten again. Likewise, when finally deleting the profile, both the storage from /run and persistent storage are deleted (or if the persistent storage does not allow deletion, and nmmeta file is written to mark the UUID as deleted).</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>this is almost the same as %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, with one difference: when later deleting the profile, the original profile will not be deleted. Instead a nmmeta file is written to /run to indicate that the profile is gone. Note that if such a nmmeta tombstone file exists and hides a file in persistent storage, then when re-adding the profile with the same UUID, then the original storage is taken over again.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>this is like %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, but if the connection has a corresponding file on persistent storage, the file will be deleted right away. If the profile is later again persisted to disk, a new, unused filename will be chosen.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_VOLATILE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>This can be specified with either %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED or %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. After making the connection in-memory only, the connection is marked as volatile. That means, if the connection is currently not active it will be deleted right away. Otherwise, it is marked to for deletion once the connection deactivates. A volatile connection cannot autoactivate again (because it's about to be deleted), but a manual activation will clear the volatile flag.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>usually, when the connection has autoconnect enabled and is modified, it becomes eligible to autoconnect right away. Setting this flag, disables autoconnect until the connection is manually activated.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x40</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>when a profile gets modified that is currently active, then these changes don't take effect for the active device unless the profile gets reactivated or the configuration reapplied. There are two exceptions: by default "connection.zone" and "connection.metered" properties take effect immediately. Specify this flag to prevent these properties to take effect, so that the change is restricted to modify the profile. Since: 1.20.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceReapplyFlags" role="enum">
-    <title>enum NMDeviceReapplyFlags</title>
-    <indexterm zone="NMDeviceReapplyFlags">
-      <primary>NMDeviceReapplyFlags</primary>
-    </indexterm>
-    <para><para>Flags for the Reapply() D-Bus call of a device and nm_device_reapply_async().</para><para>Since: 1.42</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_REAPPLY_FLAGS_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>no flag set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>during reapply, preserve external IP addresses and routes.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMTernary" role="enum">
-    <title>enum NMTernary</title>
-    <indexterm zone="NMTernary">
-      <primary>NMTernary</primary>
-    </indexterm>
-    <para><para>An boolean value that can be overridden by a default.</para><para>Since: 1.14</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_TERNARY_DEFAULT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>-1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>use the globally-configured default value.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_TERNARY_FALSE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the option is disabled.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_TERNARY_TRUE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the option is enabled.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMManagerReloadFlags" role="enum">
-    <title>enum NMManagerReloadFlags</title>
-    <indexterm zone="NMManagerReloadFlags">
-      <primary>NMManagerReloadFlags</primary>
-    </indexterm>
-    <para><para>Flags for the manager Reload() call.</para><para>Since: 1.22</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MANAGER_RELOAD_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set. This reloads everything that is supported and is identical to a SIGHUP.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MANAGER_RELOAD_FLAG_CONF</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>reload the NetworkManager.conf configuration from disk. Note that this does not include connections, which can be reloaded via Setting's ReloadConnections().</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MANAGER_RELOAD_FLAG_DNS_RC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>update DNS configuration, which usually involves writing /etc/resolv.conf anew.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MANAGER_RELOAD_FLAG_DNS_FULL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>means to restart the DNS plugin. This is for example useful when using dnsmasq plugin, which uses additional configuration in /etc/NetworkManager/dnsmasq.d. If you edit those files, you can restart the DNS plugin. This action shortly interrupts name resolution.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MANAGER_RELOAD_FLAG_ALL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>all flags.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMDeviceInterfaceFlags" role="enum">
-    <title>enum NMDeviceInterfaceFlags</title>
-    <indexterm zone="NMDeviceInterfaceFlags">
-      <primary>NMDeviceInterfaceFlags</primary>
-    </indexterm>
-    <para><para>Flags for a network interface.</para><para>Since: 1.22</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_INTERFACE_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_INTERFACE_FLAG_UP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the interface is enabled from the administrative point of view. Corresponds to kernel IFF_UP.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_INTERFACE_FLAG_LOWER_UP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the physical link is up. Corresponds to kernel IFF_LOWER_UP.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_INTERFACE_FLAG_PROMISC</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>receive all packets. Corresponds to kernel IFF_PROMISC. Since: 1.32.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_INTERFACE_FLAG_CARRIER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x10000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the interface has carrier. In most cases this is equal to the value of @NM_DEVICE_INTERFACE_FLAG_LOWER_UP. However some devices have a non-standard carrier detection mechanism.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_DEVICE_INTERFACE_FLAG_LLDP_CLIENT_ENABLED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x20000</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the flag to indicate device LLDP status. Since: 1.32.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMClientPermission" role="enum">
-    <title>enum NMClientPermission</title>
-    <indexterm zone="NMClientPermission">
-      <primary>NMClientPermission</primary>
-    </indexterm>
-    <para><para><link linkend="NMClientPermission">NMClientPermission</link> values indicate various permissions that NetworkManager clients can obtain to perform certain tasks on behalf of the current user.</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>unknown or no permission</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether networking can be globally enabled or disabled</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether Wi-Fi can be globally enabled or disabled</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether WWAN (3G) can be globally enabled or disabled</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether WiMAX can be globally enabled or disabled</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_SLEEP_WAKE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether the client can ask NetworkManager to sleep and wake</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_NETWORK_CONTROL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether networking connections can be started, stopped, and changed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether a password protected Wi-Fi hotspot can be created</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether an open Wi-Fi hotspot can be created</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether connections that are available to all users can be modified</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether connections owned by the current user can be modified</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether the persistent hostname can be changed</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>12</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>modify persistent global DNS configuration</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_RELOAD</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>13</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls access to Reload.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_CHECKPOINT_ROLLBACK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>14</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>permission to create checkpoints.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_ENABLE_DISABLE_STATISTICS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>15</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether device statistics can be globally enabled or disabled</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>16</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether connectivity check can be enabled or disabled</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_WIFI_SCAN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>17</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>controls whether wifi scans can be performed</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMClientPermissionResult" role="enum">
-    <title>enum NMClientPermissionResult</title>
-    <indexterm zone="NMClientPermissionResult">
-      <primary>NMClientPermissionResult</primary>
-    </indexterm>
-    <para><para><link linkend="NMClientPermissionResult">NMClientPermissionResult</link> values indicate what authorizations and permissions the user requires to obtain a given <link linkend="NMClientPermission">NMClientPermission</link></para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_RESULT_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>unknown or no authorization</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_RESULT_YES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the permission is available</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_RESULT_AUTH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>authorization is necessary before the permission is available</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CLIENT_PERMISSION_RESULT_NO</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>permission to perform the operation is denied by system policy</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMRadioFlags" role="enum">
-    <title>enum NMRadioFlags</title>
-    <indexterm zone="NMRadioFlags">
-      <primary>NMRadioFlags</primary>
-    </indexterm>
-    <para><para>Flags related to radio interfaces.</para><para>Since: 1.38</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_RADIO_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_RADIO_FLAG_WLAN_AVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A Wireless LAN device or rfkill switch is detected in the system.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_RADIO_FLAG_WWAN_AVAILABLE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A Wireless WAN device or rfkill switch is detected in the system.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMMptcpFlags" role="enum">
-    <title>enum NMMptcpFlags</title>
-    <indexterm zone="NMMptcpFlags">
-      <primary>NMMptcpFlags</primary>
-    </indexterm>
-    <para><para>Since: 1.40</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The default, meaning that no MPTCP flags are set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_DISABLED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>don't configure MPTCP endpoints on the device.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_ENABLED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>MPTCP is enabled and endpoints will be configured. This flag is implied if any of the other flags indicate that MPTCP is enabled and therefore in most cases unnecessary. Note that if "/proc/sys/net/mptcp/enabled" sysctl is disabled, MPTCP handling is disabled despite this flag. This can be overruled with the "also-without-sysctl" flag. Note that by default interfaces that don't have a default route are excluded from having MPTCP endpoints configured. This can be overruled with the "also-without-default-route" and this affects endpoints per address family.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_ALSO_WITHOUT_SYSCTL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>even if MPTCP handling is enabled via the "enabled" flag, it is ignored unless "/proc/sys/net/mptcp/enabled" is on. With this flag, MPTCP endpoints will be configured regardless of the sysctl setting.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_ALSO_WITHOUT_DEFAULT_ROUTE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>even if MPTCP handling is enabled via the "enabled" flag, it is ignored per-address family unless NetworkManager configures a default route. With this flag, NetworkManager will also configure MPTCP endpoints if there is no default route. This takes effect per-address family.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_SIGNAL</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Flag for the MPTCP endpoint. The endpoint will be announced/signaled to each peer via an MPTCP ADD_ADDR sub-option.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_SUBFLOW</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x20</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Flag for the MPTCP endpoint. If additional subflow creation is allowed by the MPTCP limits, the MPTCP path manager will try to create an additional subflow using this endpoint as the source address after the MPTCP connection is established.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_BACKUP</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x40</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Flag for the MPTCP endpoint. If this is a subflow endpoint, the subflows created using this endpoint will have the backup flag set during the connection process. This flag instructs the peer to only send data on a given subflow when all non-backup subflows are unavailable. This does not affect outgoing data, where subflow priority is determined by the backup/non-backup flag received from the peer</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_MPTCP_FLAGS_FULLMESH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x80</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Flag for the MPTCP endpoint. If this is a subflow endpoint and additional subflow creation is allowed by the MPTCP limits, the MPTCP path manager will try to create an additional subflow for each known peer address, using this endpoint as the source address. This will occur after the MPTCP connection is established. If the peer did not announce any additional addresses using the MPTCP ADD_ADDR sub-option, this will behave the same as a plain subflow endpoint. When the peer does announce addresses, each received ADD_ADDR sub-option will trigger creation of an additional subflow to generate a full mesh topology.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-</refentry>
diff --git a/src/libnm-core-public/nm-setting-8021x.h b/src/libnm-core-public/nm-setting-8021x.h
index d91850f7..f22a6ee5 100644
--- a/src/libnm-core-public/nm-setting-8021x.h
+++ b/src/libnm-core-public/nm-setting-8021x.h
@@ -155,6 +155,7 @@ typedef enum /*< underscore_name=nm_setting_802_1x_auth_flags, flags >*/ {
 #define NM_SETTING_802_1X_SYSTEM_CA_CERTS                   "system-ca-certs"
 #define NM_SETTING_802_1X_AUTH_TIMEOUT                      "auth-timeout"
 #define NM_SETTING_802_1X_OPTIONAL                          "optional"
+#define NM_SETTING_802_1X_OPENSSL_CIPHERS                   "openssl-ciphers"
 
 /* PRIVATE KEY NOTE: when setting PKCS#12 private keys directly via properties
  * using the "blob" scheme, the data must be passed in PKCS#12 binary format.
@@ -358,6 +359,8 @@ NM_AVAILABLE_IN_1_8
 int nm_setting_802_1x_get_auth_timeout(NMSetting8021x *setting);
 NM_AVAILABLE_IN_1_22
 gboolean nm_setting_802_1x_get_optional(NMSetting8021x *setting);
+NM_AVAILABLE_IN_1_48
+const char *nm_setting_802_1x_get_openssl_ciphers(NMSetting8021x *setting);
 
 G_END_DECLS
 
diff --git a/src/libnm-core-public/nm-setting-connection.h b/src/libnm-core-public/nm-setting-connection.h
index c7ac1659..298fb144 100644
--- a/src/libnm-core-public/nm-setting-connection.h
+++ b/src/libnm-core-public/nm-setting-connection.h
@@ -64,6 +64,7 @@ G_BEGIN_DECLS
 #define NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT   "wait-device-timeout"
 #define NM_SETTING_CONNECTION_MUD_URL               "mud-url"
 #define NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY "wait-activation-delay"
+#define NM_SETTING_CONNECTION_DOWN_ON_POWEROFF      "down-on-poweroff"
 
 /* Types for property values */
 /**
@@ -158,6 +159,23 @@ typedef enum {
     NM_SETTING_CONNECTION_DNS_OVER_TLS_YES           = 2,
 } NMSettingConnectionDnsOverTls;
 
+/**
+ * NMSettingConnectionDownOnPoweroff:
+ * @NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT: default value
+ * @NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO: disable down-on-poweroff
+ * @NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES: enable down-on-poweroff
+ *
+ * #NMSettingConnectionDownOnPoweroff indicates whether the connection will be
+ * brought down before the system is powered off.
+ *
+ * Since: 1.48
+ */
+typedef enum {
+    NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT = -1,
+    NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO      = 0,
+    NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES     = 1,
+} NMSettingConnectionDownOnPoweroff;
+
 typedef struct _NMSettingConnectionClass NMSettingConnectionClass;
 
 GType nm_setting_connection_get_type(void);
@@ -254,6 +272,10 @@ gint32 nm_setting_connection_get_wait_device_timeout(NMSettingConnection *settin
 NM_AVAILABLE_IN_1_40
 gint32 nm_setting_connection_get_wait_activation_delay(NMSettingConnection *setting);
 
+NM_AVAILABLE_IN_1_48
+NMSettingConnectionDownOnPoweroff
+nm_setting_connection_get_down_on_poweroff(NMSettingConnection *setting);
+
 NM_AVAILABLE_IN_1_26
 const char *nm_setting_connection_get_mud_url(NMSettingConnection *setting);
 
diff --git a/src/libnm-core-public/nm-setting-ip-config.h b/src/libnm-core-public/nm-setting-ip-config.h
index 40609f44..3b732882 100644
--- a/src/libnm-core-public/nm-setting-ip-config.h
+++ b/src/libnm-core-public/nm-setting-ip-config.h
@@ -342,6 +342,7 @@ char *nm_ip_routing_rule_to_string(const NMIPRoutingRule       *self,
 #define NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS "dhcp-reject-servers"
 #define NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW   "auto-route-ext-gw"
 #define NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE  "replace-local-rule"
+#define NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE   "dhcp-send-release"
 
 /* these are not real GObject properties. */
 #define NM_SETTING_IP_CONFIG_ROUTING_RULES "routing-rules"
@@ -509,6 +510,8 @@ NM_AVAILABLE_IN_1_42
 NMTernary nm_setting_ip_config_get_auto_route_ext_gw(NMSettingIPConfig *setting);
 NM_AVAILABLE_IN_1_44
 NMTernary nm_setting_ip_config_get_replace_local_rule(NMSettingIPConfig *setting);
+NM_AVAILABLE_IN_1_48
+NMTernary nm_setting_ip_config_get_dhcp_send_release(NMSettingIPConfig *setting);
 
 G_END_DECLS
 
diff --git a/src/libnm-core-public/nm-setting-ip6-config.h b/src/libnm-core-public/nm-setting-ip6-config.h
index 0403cdf8..b17fc465 100644
--- a/src/libnm-core-public/nm-setting-ip6-config.h
+++ b/src/libnm-core-public/nm-setting-ip6-config.h
@@ -30,6 +30,10 @@ G_BEGIN_DECLS
 
 #define NM_SETTING_IP6_CONFIG_IP6_PRIVACY "ip6-privacy"
 
+#define NM_SETTING_IP6_CONFIG_TEMP_VALID_LIFETIME "temp-valid-lifetime"
+
+#define NM_SETTING_IP6_CONFIG_TEMP_PREFERRED_LIFETIME "temp-preferred-lifetime"
+
 #define NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE "addr-gen-mode"
 
 #define NM_SETTING_IP6_CONFIG_TOKEN "token"
@@ -156,6 +160,10 @@ GType nm_setting_ip6_config_get_type(void);
 NMSetting *nm_setting_ip6_config_new(void);
 
 NMSettingIP6ConfigPrivacy nm_setting_ip6_config_get_ip6_privacy(NMSettingIP6Config *setting);
+NM_AVAILABLE_IN_1_48
+gint32 nm_setting_ip6_config_get_temp_valid_lifetime(NMSettingIP6Config *setting);
+NM_AVAILABLE_IN_1_48
+gint32 nm_setting_ip6_config_get_temp_preferred_lifetime(NMSettingIP6Config *setting);
 NM_AVAILABLE_IN_1_2
 NMSettingIP6ConfigAddrGenMode nm_setting_ip6_config_get_addr_gen_mode(NMSettingIP6Config *setting);
 NM_AVAILABLE_IN_1_4
diff --git a/src/libnm-core-public/nm-setting-wired.h b/src/libnm-core-public/nm-setting-wired.h
index 5ad3b7f2..dab22978 100644
--- a/src/libnm-core-public/nm-setting-wired.h
+++ b/src/libnm-core-public/nm-setting-wired.h
@@ -73,6 +73,7 @@ typedef enum /*< flags >*/ {
 #define NM_SETTING_WIRED_CLONED_MAC_ADDRESS        "cloned-mac-address"
 #define NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask"
 #define NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST     "mac-address-blacklist"
+#define NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST      "mac-address-denylist"
 #define NM_SETTING_WIRED_MTU                       "mtu"
 #define NM_SETTING_WIRED_S390_SUBCHANNELS          "s390-subchannels"
 #define NM_SETTING_WIRED_S390_NETTYPE              "s390-nettype"
@@ -99,14 +100,30 @@ NMTernary nm_setting_wired_get_accept_all_mac_addresses(NMSettingWired *setting)
 NM_AVAILABLE_IN_1_4
 const char *nm_setting_wired_get_generate_mac_address_mask(NMSettingWired *setting);
 
+NM_DEPRECATED_IN_1_48
 const char *const *nm_setting_wired_get_mac_address_blacklist(NMSettingWired *setting);
-guint32            nm_setting_wired_get_num_mac_blacklist_items(NMSettingWired *setting);
-const char        *nm_setting_wired_get_mac_blacklist_item(NMSettingWired *setting, guint32 idx);
+NM_DEPRECATED_IN_1_48
+guint32 nm_setting_wired_get_num_mac_blacklist_items(NMSettingWired *setting);
+NM_DEPRECATED_IN_1_48
+const char *nm_setting_wired_get_mac_blacklist_item(NMSettingWired *setting, guint32 idx);
+NM_DEPRECATED_IN_1_48
 gboolean nm_setting_wired_add_mac_blacklist_item(NMSettingWired *setting, const char *mac);
-void     nm_setting_wired_remove_mac_blacklist_item(NMSettingWired *setting, guint32 idx);
+NM_DEPRECATED_IN_1_48
+void nm_setting_wired_remove_mac_blacklist_item(NMSettingWired *setting, guint32 idx);
+NM_DEPRECATED_IN_1_48
 gboolean nm_setting_wired_remove_mac_blacklist_item_by_value(NMSettingWired *setting,
                                                              const char     *mac);
-void     nm_setting_wired_clear_mac_blacklist_items(NMSettingWired *setting);
+NM_DEPRECATED_IN_1_48
+void nm_setting_wired_clear_mac_blacklist_items(NMSettingWired *setting);
+
+const char *const *nm_setting_wired_get_mac_address_denylist(NMSettingWired *setting);
+guint32            nm_setting_wired_get_num_mac_denylist_items(NMSettingWired *setting);
+const char        *nm_setting_wired_get_mac_denylist_item(NMSettingWired *setting, guint32 idx);
+gboolean           nm_setting_wired_add_mac_denylist_item(NMSettingWired *setting, const char *mac);
+void               nm_setting_wired_remove_mac_denylist_item(NMSettingWired *setting, guint32 idx);
+gboolean           nm_setting_wired_remove_mac_denylist_item_by_value(NMSettingWired *setting,
+                                                                      const char     *mac);
+void               nm_setting_wired_clear_mac_denylist_items(NMSettingWired *setting);
 
 guint32 nm_setting_wired_get_mtu(NMSettingWired *setting);
 
diff --git a/src/libnm-core-public/nm-setting-wireless.h b/src/libnm-core-public/nm-setting-wireless.h
index 5f8d09c5..e240a354 100644
--- a/src/libnm-core-public/nm-setting-wireless.h
+++ b/src/libnm-core-public/nm-setting-wireless.h
@@ -84,6 +84,7 @@ typedef enum /*< flags >*/ {
 #define NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS        "cloned-mac-address"
 #define NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask"
 #define NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST     "mac-address-blacklist"
+#define NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST      "mac-address-denylist"
 #define NM_SETTING_WIRELESS_MTU                       "mtu"
 #define NM_SETTING_WIRELESS_SEEN_BSSIDS               "seen-bssids"
 #define NM_SETTING_WIRELESS_HIDDEN                    "hidden"
@@ -168,14 +169,30 @@ const char *nm_setting_wireless_get_cloned_mac_address(NMSettingWireless *settin
 NM_AVAILABLE_IN_1_4
 const char *nm_setting_wireless_get_generate_mac_address_mask(NMSettingWireless *setting);
 
+NM_DEPRECATED_IN_1_48
 const char *const *nm_setting_wireless_get_mac_address_blacklist(NMSettingWireless *setting);
-guint32            nm_setting_wireless_get_num_mac_blacklist_items(NMSettingWireless *setting);
+NM_DEPRECATED_IN_1_48
+guint32 nm_setting_wireless_get_num_mac_blacklist_items(NMSettingWireless *setting);
+NM_DEPRECATED_IN_1_48
 const char *nm_setting_wireless_get_mac_blacklist_item(NMSettingWireless *setting, guint32 idx);
-gboolean    nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const char *mac);
-void        nm_setting_wireless_remove_mac_blacklist_item(NMSettingWireless *setting, guint32 idx);
-gboolean    nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *setting,
-                                                                   const char        *mac);
-void        nm_setting_wireless_clear_mac_blacklist_items(NMSettingWireless *setting);
+NM_DEPRECATED_IN_1_48
+gboolean nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const char *mac);
+NM_DEPRECATED_IN_1_48
+void nm_setting_wireless_remove_mac_blacklist_item(NMSettingWireless *setting, guint32 idx);
+NM_DEPRECATED_IN_1_48
+gboolean nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *setting,
+                                                                const char        *mac);
+NM_DEPRECATED_IN_1_48
+void nm_setting_wireless_clear_mac_blacklist_items(NMSettingWireless *setting);
+
+const char *const *nm_setting_wireless_get_mac_address_denylist(NMSettingWireless *setting);
+guint32            nm_setting_wireless_get_num_mac_denylist_items(NMSettingWireless *setting);
+const char *nm_setting_wireless_get_mac_denylist_item(NMSettingWireless *setting, guint32 idx);
+gboolean    nm_setting_wireless_add_mac_denylist_item(NMSettingWireless *setting, const char *mac);
+void        nm_setting_wireless_remove_mac_denylist_item(NMSettingWireless *setting, guint32 idx);
+gboolean    nm_setting_wireless_remove_mac_denylist_item_by_value(NMSettingWireless *setting,
+                                                                  const char        *mac);
+void        nm_setting_wireless_clear_mac_denylist_items(NMSettingWireless *setting);
 
 guint32  nm_setting_wireless_get_mtu(NMSettingWireless *setting);
 gboolean nm_setting_wireless_get_hidden(NMSettingWireless *setting);
diff --git a/src/libnm-core-public/nm-version-macros.h b/src/libnm-core-public/nm-version-macros.h
deleted file mode 100644
index 7e0b4a36..00000000
--- a/src/libnm-core-public/nm-version-macros.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2011, 2015 Red Hat, Inc.
- */
-
-#ifndef __NM_VERSION_MACROS_H__
-#define __NM_VERSION_MACROS_H__
-
-/* This header must not include glib or libnm. */
-
-/**
- * NM_MAJOR_VERSION:
- *
- * Evaluates to the major version number of NetworkManager which this source
- * is compiled against.
- */
-#define NM_MAJOR_VERSION (1)
-
-/**
- * NM_MINOR_VERSION:
- *
- * Evaluates to the minor version number of NetworkManager which this source
- * is compiled against.
- */
-#define NM_MINOR_VERSION (46)
-
-/**
- * NM_MICRO_VERSION:
- *
- * Evaluates to the micro version number of NetworkManager which this source
- * compiled against.
- */
-#define NM_MICRO_VERSION (0)
-
-/**
- * NM_CHECK_VERSION:
- * @major: major version (e.g. 1 for version 1.2.5)
- * @minor: minor version (e.g. 2 for version 1.2.5)
- * @micro: micro version (e.g. 5 for version 1.2.5)
- *
- * Returns: %TRUE if the version of the NetworkManager header files
- * is the same as or newer than the passed-in version.
- */
-#define NM_CHECK_VERSION(major, minor, micro)                                                       \
-    (NM_MAJOR_VERSION > (major) ||                                                                  \
-     (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) ||                                 \
-     (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro)))
-
-#define NM_ENCODE_VERSION(major, minor, micro) ((major) << 16 | (minor) << 8 | (micro))
-
-#define NM_VERSION_0_9_8  (NM_ENCODE_VERSION(0, 9, 8))
-#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION(0, 9, 10))
-#define NM_VERSION_1_0    (NM_ENCODE_VERSION(1, 0, 0))
-#define NM_VERSION_1_2    (NM_ENCODE_VERSION(1, 2, 0))
-#define NM_VERSION_1_4    (NM_ENCODE_VERSION(1, 4, 0))
-#define NM_VERSION_1_6    (NM_ENCODE_VERSION(1, 6, 0))
-#define NM_VERSION_1_8    (NM_ENCODE_VERSION(1, 8, 0))
-#define NM_VERSION_1_10   (NM_ENCODE_VERSION(1, 10, 0))
-#define NM_VERSION_1_12   (NM_ENCODE_VERSION(1, 12, 0))
-#define NM_VERSION_1_14   (NM_ENCODE_VERSION(1, 14, 0))
-#define NM_VERSION_1_16   (NM_ENCODE_VERSION(1, 16, 0))
-#define NM_VERSION_1_18   (NM_ENCODE_VERSION(1, 18, 0))
-#define NM_VERSION_1_20   (NM_ENCODE_VERSION(1, 20, 0))
-#define NM_VERSION_1_22   (NM_ENCODE_VERSION(1, 22, 0))
-#define NM_VERSION_1_24   (NM_ENCODE_VERSION(1, 24, 0))
-#define NM_VERSION_1_26   (NM_ENCODE_VERSION(1, 26, 0))
-#define NM_VERSION_1_28   (NM_ENCODE_VERSION(1, 28, 0))
-#define NM_VERSION_1_30   (NM_ENCODE_VERSION(1, 30, 0))
-#define NM_VERSION_1_32   (NM_ENCODE_VERSION(1, 32, 0))
-#define NM_VERSION_1_34   (NM_ENCODE_VERSION(1, 34, 0))
-#define NM_VERSION_1_36   (NM_ENCODE_VERSION(1, 36, 0))
-#define NM_VERSION_1_38   (NM_ENCODE_VERSION(1, 38, 0))
-#define NM_VERSION_1_40   (NM_ENCODE_VERSION(1, 40, 0))
-#define NM_VERSION_1_42   (NM_ENCODE_VERSION(1, 42, 0))
-#define NM_VERSION_1_44   (NM_ENCODE_VERSION(1, 44, 0))
-#define NM_VERSION_1_46   (NM_ENCODE_VERSION(1, 46, 0))
-
-/* For releases, NM_API_VERSION is equal to NM_VERSION.
- *
- * For development builds, NM_API_VERSION is the next
- * stable API after NM_VERSION. When you run a development
- * version, you are already using the future API, even if
- * it is not yet released. Hence, the currently used API
- * version is the future one.  */
-#define NM_API_VERSION                                                                                  \
-    (((NM_MINOR_VERSION % 2) == 1)                                                                      \
-        ? NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION + 1, 0                               )  \
-        : NM_ENCODE_VERSION (NM_MAJOR_VERSION, NM_MINOR_VERSION    , ((NM_MICRO_VERSION + 1) / 2) * 2))
-
-/* deprecated. */
-#define NM_VERSION_CUR_STABLE NM_API_VERSION
-
-/* deprecated. */
-#define NM_VERSION_NEXT_STABLE NM_API_VERSION
-
-#define NM_VERSION NM_ENCODE_VERSION(NM_MAJOR_VERSION, NM_MINOR_VERSION, NM_MICRO_VERSION)
-
-#endif /* __NM_VERSION_MACROS_H__ */
diff --git a/src/libnm-core-public/nm-version-macros.h.in b/src/libnm-core-public/nm-version-macros.h.in
index 072b8ca5..7967f280 100644
--- a/src/libnm-core-public/nm-version-macros.h.in
+++ b/src/libnm-core-public/nm-version-macros.h.in
@@ -74,6 +74,7 @@
 #define NM_VERSION_1_42   (NM_ENCODE_VERSION(1, 42, 0))
 #define NM_VERSION_1_44   (NM_ENCODE_VERSION(1, 44, 0))
 #define NM_VERSION_1_46   (NM_ENCODE_VERSION(1, 46, 0))
+#define NM_VERSION_1_48   (NM_ENCODE_VERSION(1, 48, 0))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/src/libnm-core-public/nm-version.h b/src/libnm-core-public/nm-version.h
index 24471129..419da2e1 100644
--- a/src/libnm-core-public/nm-version.h
+++ b/src/libnm-core-public/nm-version.h
@@ -383,6 +383,20 @@
 #define NM_AVAILABLE_IN_1_46
 #endif
 
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_48
+#define NM_DEPRECATED_IN_1_48        G_DEPRECATED
+#define NM_DEPRECATED_IN_1_48_FOR(f) G_DEPRECATED_FOR(f)
+#else
+#define NM_DEPRECATED_IN_1_48
+#define NM_DEPRECATED_IN_1_48_FOR(f)
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_48
+#define NM_AVAILABLE_IN_1_48 G_UNAVAILABLE(1, 48)
+#else
+#define NM_AVAILABLE_IN_1_48
+#endif
+
 /*
  * Synchronous API for calling D-Bus in libnm is deprecated. See
  * https://networkmanager.dev/docs/libnm/latest/usage.html#sync-api
diff --git a/src/libnm-core-public/nm-vpn-dbus-types.xml b/src/libnm-core-public/nm-vpn-dbus-types.xml
deleted file mode 100644
index 5da02420..00000000
--- a/src/libnm-core-public/nm-vpn-dbus-types.xml
+++ /dev/null
@@ -1,246 +0,0 @@
-<?xml version='1.0'?>
-<?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?>
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
-<refentry id="nm-vpn-dbus-types">
-  <refmeta>
-    <refentrytitle role="top_of_page" id="nm-vpn-dbus-types.top_of_page">VPN Plugin D-Bus API Types</refentrytitle>
-    <manvolnum>3</manvolnum>
-    <refmiscinfo>VPN Plugin D-Bus API Types</refmiscinfo>
-  </refmeta>
-  <refnamediv>
-    <refname>VPN Plugin D-Bus API Types</refname>
-    <refpurpose></refpurpose>
-  </refnamediv>
-
-  <refsect2 id="NMVpnServiceState" role="enum">
-    <title>enum NMVpnServiceState</title>
-    <indexterm zone="NMVpnServiceState">
-      <primary>NMVpnServiceState</primary>
-    </indexterm>
-    <para><para>VPN daemon states</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The state of the VPN plugin is unknown.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_INIT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN plugin is initialized.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_SHUTDOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Not used.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_STARTING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The plugin is attempting to connect to a VPN server.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_STARTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The plugin has connected to a VPN server.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_STOPPING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The plugin is disconnecting from the VPN server.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_SERVICE_STATE_STOPPED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The plugin has disconnected from the VPN server.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMVpnConnectionState" role="enum">
-    <title>enum NMVpnConnectionState</title>
-    <indexterm zone="NMVpnConnectionState">
-      <primary>NMVpnConnectionState</primary>
-    </indexterm>
-    <para><para>VPN connection states</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The state of the VPN connection is unknown.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_PREPARE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection is preparing to connect.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_NEED_AUTH</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection needs authorization credentials.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_CONNECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection is being established.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_IP_CONFIG_GET</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection is getting an IP address.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_ACTIVATED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection is active.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection is disconnected.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMVpnConnectionStateReason" role="enum">
-    <title>enum NMVpnConnectionStateReason</title>
-    <indexterm zone="NMVpnConnectionStateReason">
-      <primary>NMVpnConnectionStateReason</primary>
-    </indexterm>
-    <para><para>VPN connection state reasons</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_UNKNOWN</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The reason for the VPN connection state change is unknown.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>No reason was given for the VPN connection state change.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection changed state because the user disconnected it.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The VPN connection changed state because the device it was using was disconnected.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>4</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The service providing the VPN connection was stopped.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>5</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The IP config of the VPN connection was invalid.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>6</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The connection attempt to the VPN service timed out.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>7</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A timeout occurred while starting the service providing the VPN connection.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>8</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Starting the service starting the service providing the VPN connection failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>9</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Necessary secrets for the VPN connection were not provided.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>10</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Authentication to the VPN server failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>11</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The connection was deleted from settings.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-  <refsect2 id="NMVpnPluginFailure" role="enum">
-    <title>enum NMVpnPluginFailure</title>
-    <indexterm zone="NMVpnPluginFailure">
-      <primary>NMVpnPluginFailure</primary>
-    </indexterm>
-    <para><para>VPN plugin failure reasons</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Login failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Connect failed.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>Invalid IP configuration returned from the VPN plugin.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
-</refentry>
diff --git a/src/libnm-crypto/nm-crypto.c b/src/libnm-crypto/nm-crypto.c
index 7fcff4a9..883cf3c9 100644
--- a/src/libnm-crypto/nm-crypto.c
+++ b/src/libnm-crypto/nm-crypto.c
@@ -47,11 +47,11 @@
 
 static const NMCryptoCipherInfo cipher_infos[] = {
 #define _CI(_cipher, _name, _digest_len, _real_iv_len) \
-    [(_cipher) -1] = {.cipher      = _cipher,          \
-                      .name        = ""_name           \
-                                     "",               \
-                      .digest_len  = _digest_len,      \
-                      .real_iv_len = _real_iv_len}
+    [(_cipher) - 1] = {.cipher      = _cipher,         \
+                       .name        = ""_name          \
+                                      "",              \
+                       .digest_len  = _digest_len,     \
+                       .real_iv_len = _real_iv_len}
     _CI(NM_CRYPTO_CIPHER_DES_EDE3_CBC, "DES-EDE3-CBC", 24, 8),
     _CI(NM_CRYPTO_CIPHER_DES_CBC, "DES-CBC", 8, 8),
     _CI(NM_CRYPTO_CIPHER_AES_128_CBC, "AES-128-CBC", 16, 16),
diff --git a/src/libnm-glib-aux/README.md b/src/libnm-glib-aux/README.md
new file mode 100644
index 00000000..cb43e87c
--- /dev/null
+++ b/src/libnm-glib-aux/README.md
@@ -0,0 +1,16 @@
+libnm-glib-aux
+==============
+
+A static helper library with general purpose helpers on top
+of glib.
+
+This is similar to libnm-std-aux (on which this library depends).
+The difference is that libnm-std-aux only requires standard C (C11),
+while this has a dependency on glib.
+
+As this has no additional dependencies, we should have all our glib code
+use this internal helper library. It contains helpers that should be
+available (and used) in all our C/glib applications/libraries.
+
+Parts of this library are usually already included via the `nm-default*.h`
+headers.
diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h
index 1c8c85e6..0b39271e 100644
--- a/src/libnm-glib-aux/nm-macros-internal.h
+++ b/src/libnm-glib-aux/nm-macros-internal.h
@@ -964,8 +964,8 @@ nm_g_variant_equal(GVariant *a, GVariant *b)
 
 /* check if @flags has exactly one flag (@check) set. You should call this
  * only with @check being a compile time constant and a power of two. */
-#define NM_FLAGS_HAS(flags, check)                                       \
-    (G_STATIC_ASSERT_EXPR((check) > 0 && ((check) & ((check) -1)) == 0), \
+#define NM_FLAGS_HAS(flags, check)                                        \
+    (G_STATIC_ASSERT_EXPR((check) > 0 && ((check) & ((check) - 1)) == 0), \
      NM_FLAGS_ANY((flags), (check)))
 
 #define NM_FLAGS_ANY(flags, check) (((flags) & (check)) != 0)
@@ -1695,7 +1695,7 @@ nm_decode_version(guint version, guint *major, guint *minor, guint *micro)
 
 /*****************************************************************************/
 
-#define NM_PID_T_INVAL ((pid_t) -1)
+#define NM_PID_T_INVAL ((pid_t) - 1)
 
 /*****************************************************************************/
 
diff --git a/src/libnm-glib-aux/nm-uuid.c b/src/libnm-glib-aux/nm-uuid.c
index df1b10c0..e39d2ea5 100644
--- a/src/libnm-glib-aux/nm-uuid.c
+++ b/src/libnm-glib-aux/nm-uuid.c
@@ -415,8 +415,7 @@ nm_uuid_generate_from_string_str(const char   *s,
  *   case the result is different from an empty array.
  * @len: if negative, @strv is a NULL terminated array. Otherwise,
  *   it is the length of the strv array. In the latter case it may
- *   also contain NULL strings. The result hashes differently depending
- *   on whether we have a NULL terminated strv array or given length.
+ *   also contain NULL strings.
  *
  * Returns a @uuid_type UUID based on the concatenated C strings.
  * It does not simply concatenate them, but also includes the
@@ -436,7 +435,7 @@ nm_uuid_generate_from_strings_strv(NMUuidType         uuid_type,
     gsize                    slen;
     const char              *s;
 
-    if (len >= 0) {
+    if (len > 0) {
         gboolean has_nulls = FALSE;
         gssize   i;
 
@@ -471,14 +470,14 @@ nm_uuid_generate_from_strings_strv(NMUuidType         uuid_type,
          * in the other cases). */
         slen = 1;
         s    = "x";
-    } else if (!strv[0]) {
+    } else if (!strv[0] || len == 0) {
         slen = 0;
         s    = "";
     } else if (!strv[1]) {
         slen = strlen(strv[0]) + 1u;
         s    = strv[0];
     } else {
-        /* We concatenate the NUL termiated string, including the NUL
+        /* We concatenate the NUL terminated string, including the NUL
          * character. This way, ("a","a"), ("aa"), ("aa","") all hash
          * differently. */
         for (; strv[0]; strv++)
diff --git a/src/libnm-lldp/nm-lldp-neighbor.c b/src/libnm-lldp/nm-lldp-neighbor.c
index a2a9695e..0379cf38 100644
--- a/src/libnm-lldp/nm-lldp-neighbor.c
+++ b/src/libnm-lldp/nm-lldp-neighbor.c
@@ -648,9 +648,10 @@ nm_lldp_neighbor_tlv_get_oui(NMLldpNeighbor *n, uint8_t oui[static 3], uint8_t *
     int            r;
 
     g_return_val_if_fail(n, -EINVAL);
-    g_return_val_if_fail(oui, -EINVAL);
     g_return_val_if_fail(subtype, -EINVAL);
 
+    nm_assert(oui);
+
     r = nm_lldp_neighbor_tlv_is_type(n, NM_LLDP_TYPE_PRIVATE);
     if (r < 0)
         return r;
diff --git a/src/libnm-lldp/nm-lldp-network.c b/src/libnm-lldp/nm-lldp-network.c
index 811c3a72..28cc7452 100644
--- a/src/libnm-lldp/nm-lldp-network.c
+++ b/src/libnm-lldp/nm-lldp-network.c
@@ -46,7 +46,7 @@ nm_lldp_network_bind_raw_socket(int ifindex)
 
     assert(ifindex > 0);
 
-    fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htobe16(NM_ETHERTYPE_LLDP));
+    fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htobe16(ETH_P_ALL));
     if (fd < 0)
         return -errno;
 
diff --git a/src/libnm-log-core/README.md b/src/libnm-log-core/README.md
new file mode 100644
index 00000000..4223232b
--- /dev/null
+++ b/src/libnm-log-core/README.md
@@ -0,0 +1,10 @@
+libnm-log-core
+==============
+
+libnm-glib-aux has a forward-declaration of logging API.
+If a libnm-glib-aux user uses that API for logging, it must
+link the final binary with an implementation.
+
+There are two implementations: libnm-log-core and
+[../libnm-log-null/(..libnm-log-null/). This one is the implementation
+used by the daemon and logs to syslog/journald.
diff --git a/src/libnm-log-null/README.md b/src/libnm-log-null/README.md
new file mode 100644
index 00000000..eb5d0821
--- /dev/null
+++ b/src/libnm-log-null/README.md
@@ -0,0 +1,10 @@
+libnm-log-null
+==============
+
+libnm-glib-aux has a forward-declaration of logging API.
+If a libnm-glib-aux user uses that API for logging, it must
+link the final binary with an implementation.
+
+There are two implementations: libnm-log-null and
+[../libnm-log-core/(..libnm-log-core/). This one is a dummy implementation
+that drops all logging.
diff --git a/src/libnm-platform/README.md b/src/libnm-platform/README.md
new file mode 100644
index 00000000..c8e7cf76
--- /dev/null
+++ b/src/libnm-platform/README.md
@@ -0,0 +1,22 @@
+libnm-platform
+==============
+
+A static helper library that provides `NMPlatform` and other utils.
+This is NetworkManager's internal netlink library, but also contains
+helpers for sysfs, ethtool and other kernel APIs.
+
+`NMPlaform` is also a cache of objects of the netlink API: `NMPCache`
+and `NMPObject`. These objects are used throughout NetworkManager
+also for generally tracking information about these types. For example,
+`NMPlatformIP4Address` (the public part of a certain type of `NMPObject`)
+is not only used to track platform addresses from netlink in the cache,
+but to track information about IPv4 addresses in general.
+
+This depends on the following helper libraries
+
+  - [../libnm-std-aux/](../libnm-std-aux/)
+  - [../libnm-base/](../libnm-base/)
+  - [../libnm-glib-aux/](../libnm-glib-aux/)
+  - [../libnm-udev-aux/](../libnm-udev-aux/)
+  - [../libnm-log-core/](../libnm-log-core/)
+  - [../linux-headers/](../linux-headers/)
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index 9ecac2d9..5b595a9b 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -3903,6 +3903,34 @@ _new_from_nl_addr(const struct nlmsghdr *nlh, gboolean id_only)
     return g_steal_pointer(&obj);
 }
 
+static gboolean
+ip_route_is_tracked(guint8 proto, guint8 type)
+{
+    if (proto > RTPROT_STATIC && !NM_IN_SET(proto, RTPROT_DHCP, RTPROT_RA)) {
+        /* We ignore certain rtm_protocol, because NetworkManager would only ever
+         * configure certain protocols. Other routes are not configured by NetworkManager
+         * and we don't track them in the platform cache.
+         *
+         * This is to help with the performance overhead of a huge number of
+         * routes, for example with the bird BGP software, that adds routes
+         * with RTPROT_BIRD protocol. */
+        return FALSE;
+    }
+
+    if (!NM_IN_SET(type,
+                   RTN_UNICAST,
+                   RTN_LOCAL,
+                   RTN_BLACKHOLE,
+                   RTN_UNREACHABLE,
+                   RTN_PROHIBIT,
+                   RTN_THROW)) {
+        /* Certain route types are ignored and not placed into the cache. */
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 /* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */
 static NMPObject *
 _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter)
@@ -3963,6 +3991,16 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
      * only handle ~supported~ routes.
      *****************************************************************/
 
+    /* If it's a route that we don't need to track, abort here to avoid unnecessary
+     * memory allocations to create the nmp_object. However, if the message has the
+     * NLM_F_REPLACE flag, it might be replacing a route that we were tracking so we
+     * have to stop tracking it. That means that we have to process all messages with
+     * NLM_F_REPLACE. See nmp_cache_update_netlink_route().
+     */
+    if (!ip_route_is_tracked(rtm->rtm_protocol, rtm->rtm_type)
+        && !(nlh->nlmsg_flags & NLM_F_REPLACE))
+        return NULL;
+
     addr_family = rtm->rtm_family;
 
     if (addr_family == AF_INET)
@@ -5519,39 +5557,18 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route)
 static gboolean
 ip_route_is_alive(const NMPlatformIPRoute *route)
 {
-    guint8 prot;
+    guint8 proto, type;
 
     nm_assert(route);
     nm_assert(route->rt_source >= NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC
               && route->rt_source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST);
 
-    prot = route->rt_source - 1;
-
-    nm_assert(nmp_utils_ip_config_source_from_rtprot(prot) == route->rt_source);
-
-    if (prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA)) {
-        /* We ignore certain rtm_protocol, because NetworkManager would only ever
-         * configure certain protocols. Other routes are not configured by NetworkManager
-         * and we don't track them in the platform cache.
-         *
-         * This is to help with the performance overhead of a huge number of
-         * routes, for example with the bird BGP software, that adds routes
-         * with RTPROT_BIRD protocol. */
-        return FALSE;
-    }
+    proto = route->rt_source - 1;
+    type  = nm_platform_route_type_uncoerce(route->type_coerced);
 
-    if (!NM_IN_SET(nm_platform_route_type_uncoerce(route->type_coerced),
-                   RTN_UNICAST,
-                   RTN_LOCAL,
-                   RTN_BLACKHOLE,
-                   RTN_UNREACHABLE,
-                   RTN_PROHIBIT,
-                   RTN_THROW)) {
-        /* Certain route types are ignored and not placed into the cache. */
-        return FALSE;
-    }
+    nm_assert(nmp_utils_ip_config_source_from_rtprot(proto) == route->rt_source);
 
-    return TRUE;
+    return ip_route_is_tracked(proto, type);
 }
 
 /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */
diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c
index 5684b8cd..6d153128 100644
--- a/src/libnm-platform/nm-netlink.c
+++ b/src/libnm-platform/nm-netlink.c
@@ -4,6 +4,7 @@
  */
 
 #include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
+#include "libnm-glib-aux/nm-random-utils.h"
 
 #include "nm-netlink.h"
 
@@ -1105,7 +1106,7 @@ nl_socket_new(struct nl_sock **out_sk,
 {
     nm_auto_nlsock struct nl_sock *sk = NULL;
     nm_auto_close int              fd = -1;
-    time_t                         t;
+    unsigned                       seq_init;
     int                            err;
     int                            nmerr;
     socklen_t                      addrlen;
@@ -1121,7 +1122,7 @@ nl_socket_new(struct nl_sock **out_sk,
     if (fd < 0)
         return -nm_errno_from_native(errno);
 
-    t = time(NULL);
+    nm_random_get_bytes(&seq_init, sizeof(seq_init));
 
     sk  = g_slice_new(struct nl_sock);
     *sk = (struct nl_sock){
@@ -1138,8 +1139,8 @@ nl_socket_new(struct nl_sock **out_sk,
                 .nl_family = AF_NETLINK,
                 .nl_groups = 0,
             },
-        .s_seq_expect = t,
-        .s_seq_next   = t,
+        .s_seq_expect = seq_init,
+        .s_seq_next   = seq_init,
         .s_bufsize    = 0,
         .s_msg_peek   = !NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK),
         .s_auto_ack   = TRUE,
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index b89b0359..cd5a54bb 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -6147,9 +6147,9 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
         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))
+                                            _nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv),
+                                            str_addrmode,
+                                            sizeof(str_addrmode))
                                       : "",
         str_address[0] ? " addr " : "",
         str_address[0] ? str_address : "",
@@ -7385,11 +7385,12 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz
                                                        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)))
-                       : "");
+        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;
 }
diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c
index 4090da71..cb4e9764 100644
--- a/src/libnm-platform/nmp-object.c
+++ b/src/libnm-platform/nmp-object.c
@@ -2988,6 +2988,13 @@ nmp_cache_update_netlink_route(NMPCache         *cache,
          * Since we don't cache all routes (see "route_is_alive"), we cannot know
          * with certainty which route was replaced.
          *
+         * For example, the kernel might have 3 similar routes (same WEAK_ID), one
+         * of which is not tracked by us so we don't have it into the cache. If we
+         * receive a route replace message, we don't know to what of the 3 routes
+         * it affects (one of the 3 we don't even know that exists). Moreover, if
+         * we only have one route on cache, we don't know if the replace is for a
+         * different one that we don't track.
+         *
          * Even if we would cache *all* routes (which we cannot, if kernel adds new
          * routing features that modify the known nmp_object_id_equal()), it would
          * be hard to find the right route that was replaced. Well, probably we
@@ -3002,15 +3009,14 @@ nmp_cache_update_netlink_route(NMPCache         *cache,
          * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860
          *
          * We need to resync.
+         *
+         * However, a resync is expensive. Think of a routing daemon that updates
+         * hundreds of routes per second, the performance penalty is huge. We can
+         * optimize it: if we don't have any matching route on cache (by WEAK_ID),
+         * we don't have anything to replace and we don't need a full resync, but
+         * only to add or discard the new route as usual.
          */
-        if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_IP4_ROUTE
-            && !nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
-            /* For IPv4, we can do a small optimization. We skip the resync, if we have
-             * no conflicting routes (by weak-id).
-             *
-             * This optimization does not work for IPv6 (maybe should be fixed).
-             */
-        } else {
+        if (nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
             entry_replace   = NULL;
             resync_required = TRUE;
             goto out;
diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c
index 6109849a..3c00898a 100644
--- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c
+++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c
@@ -567,6 +567,7 @@ struct nl80211_device_info {
     int                 phy;
     Nl80211Freq        *freqs;
     int                 num_freqs;
+    int                 num_freqs_alloc;
     guint32             freq;
     guint32             caps;
     gboolean            can_scan;
@@ -610,7 +611,6 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
     struct nlattr              *nl_freq;
     int                         rem_freq;
     int                         rem_band;
-    guint                       num_alloc;
 
 #ifdef NL80211_FREQUENCY_ATTR_NO_IR
     G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR
@@ -622,22 +622,16 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
     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)
+    if (!tb[NL80211_ATTR_WIPHY])
         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]) {
+    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;
@@ -664,51 +658,57 @@ nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
         }
     }
 
-    /* Read supported frequencies */
-    num_alloc       = 32;
-    info->num_freqs = 0;
-    info->freqs     = g_new(Nl80211Freq, num_alloc);
+    if (tb[NL80211_ATTR_WIPHY_BANDS]) {
+        /* Read supported frequencies */
 
-    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;
+        if (!info->freqs) {
+            info->num_freqs       = 0;
+            info->num_freqs_alloc = 32;
+            info->freqs           = g_new(Nl80211Freq, info->num_freqs_alloc);
+        }
 
-        nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
-            Nl80211Freq *f;
+        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;
 
-            if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
+            if (!tb_band[NL80211_BAND_ATTR_FREQS])
                 continue;
 
-            if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                continue;
+            nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+                Nl80211Freq *f;
 
-            if (info->num_freqs >= num_alloc) {
-                num_alloc *= 2;
-                info->freqs = g_renew(Nl80211Freq, info->freqs, num_alloc);
-            }
+                if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0)
+                    continue;
+
+                if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                    continue;
 
-            f  = &info->freqs[info->num_freqs];
-            *f = (Nl80211Freq){
-                .freq     = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]),
-                .disabled = !!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED],
-                .no_ir    = !!tb_freq[NL80211_FREQUENCY_ATTR_NO_IR],
-            };
+                if (info->num_freqs >= info->num_freqs_alloc) {
+                    info->num_freqs_alloc *= 2;
+                    info->freqs = g_renew(Nl80211Freq, info->freqs, info->num_freqs_alloc);
+                }
 
-            info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID;
+                f  = &info->freqs[info->num_freqs];
+                *f = (Nl80211Freq){
+                    .freq     = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]),
+                    .disabled = !!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED],
+                    .no_ir    = !!tb_freq[NL80211_FREQUENCY_ATTR_NO_IR],
+                };
 
-            if (f->freq >= 2401 && f->freq <= 2495)
-                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
-            if (f->freq >= 5150 && f->freq <= 5895)
-                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
-            if (f->freq >= 5925 && f->freq <= 7125)
-                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_6GHZ;
+                info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID;
 
-            info->num_freqs++;
+                if (f->freq >= 2401 && f->freq <= 2495)
+                    info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ;
+                if (f->freq >= 5150 && f->freq < 5950)
+                    info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
+                if (f->freq >= 5950 && f->freq <= 7125)
+                    info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_6GHZ;
+
+                info->num_freqs++;
+            }
         }
     }
 
-    info->freqs = g_renew(Nl80211Freq, info->freqs, info->num_freqs);
-
     /* Read security/encryption support */
     if (tb[NL80211_ATTR_CIPHER_SUITES]) {
         guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]);
@@ -874,7 +874,10 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
 
     self->phy = -1;
 
-    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0);
+    msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, NLM_F_DUMP);
+    NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
+
+    device_info.can_scan_ssid = TRUE;
 
     device_info.self = self;
     if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) {
@@ -882,6 +885,10 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
         return NULL;
     }
 
+    if (device_info.freqs) {
+        device_info.freqs = g_renew(Nl80211Freq, device_info.freqs, device_info.num_freqs);
+    }
+
     if (!device_info.success) {
         _LOGD("NL80211_CMD_GET_WIPHY request indicated failure");
         return NULL;
@@ -915,4 +922,7 @@ nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifin
 
     _LOGD("using nl80211 for Wi-Fi device control");
     return (NMWifiUtils *) g_steal_pointer(&self);
+
+nla_put_failure:
+    g_return_val_if_reached(NULL);
 }
diff --git a/src/libnm-std-aux/README.md b/src/libnm-std-aux/README.md
new file mode 100644
index 00000000..ccc48e9d
--- /dev/null
+++ b/src/libnm-std-aux/README.md
@@ -0,0 +1,16 @@
+libnm-std-aux
+=============
+
+A static helper library with general purpose helpers on top of
+standard C (C11).
+
+As this has no additional dependencies, we should have all our C code
+use this internal helper library. It contains helpers that should be
+available (and used) everywhere where we write C.
+
+Our C is gnu11, that is C11 or newer with some GCC-ism. The requirement
+is that it is supported by all complilers we care about (in pratice GCC
+and Clang).
+
+Parts of this library are usually already included via the `nm-default*.h`
+headers.
diff --git a/src/libnm-std-aux/nm-linux-compat.h b/src/libnm-std-aux/nm-linux-compat.h
index 2b04b0df..9bfdb365 100644
--- a/src/libnm-std-aux/nm-linux-compat.h
+++ b/src/libnm-std-aux/nm-linux-compat.h
@@ -16,7 +16,7 @@
 #include <linux/const.h>
 
 #ifndef __KERNEL_DIV_ROUND_UP
-#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) -1) / (d))
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
 #endif
 
 #include "linux-headers/ethtool.h"
diff --git a/src/libnm-systemd-core/README.md b/src/libnm-systemd-core/README.md
new file mode 100644
index 00000000..e47eb8ed
--- /dev/null
+++ b/src/libnm-systemd-core/README.md
@@ -0,0 +1,23 @@
+libnm-systemd-core
+==================
+
+This is a fork of systemd source files that are compiled
+as a static library with network helpers.
+
+We use systemd's DHCPv6 and LLDP library, by forking their code.
+
+We also still use their DHCPv4 library, but that is about to be replaced
+by nettools' n-dhcp4 and not used unless you configure the undocumented
+`[main].dhcp=systemd` plugin.
+
+This approach of code-reuse is very cumbersome, and we should replace
+systemd code by a proper library (like [nettools](https://github.com/nettools/)).
+
+We should not use systemd directly from our sources, beyond what
+we really need.
+
+
+Reimport Upstream Code
+----------------------
+
+Read [here](../libnm-systemd-shared/README.md#reimport-upstream-code).
diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build
index 6175e42c..15e1c8a6 100644
--- a/src/libnm-systemd-core/meson.build
+++ b/src/libnm-systemd-core/meson.build
@@ -3,18 +3,21 @@
 libnm_systemd_core = static_library(
   'nm-systemd-core',
   sources: files(
-    'src/libsystemd-network/dhcp-identifier.c',
     'src/libsystemd-network/dhcp6-network.c',
     'src/libsystemd-network/dhcp6-option.c',
     'src/libsystemd-network/dhcp6-protocol.c',
     'src/libsystemd-network/network-common.c',
+    'src/libsystemd-network/sd-dhcp-duid.c',
     'src/libsystemd-network/sd-dhcp6-client.c',
     'src/libsystemd-network/sd-dhcp6-lease.c',
+    'src/libsystemd/sd-device/device-private.c',
+    'src/libsystemd/sd-device/sd-device.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',
+    'sd-adapt-core/netif-util.c',
     'sd-adapt-core/nm-sd-adapt-core.c',
   ),
   include_directories: [
@@ -28,6 +31,7 @@ libnm_systemd_core = static_library(
     top_inc,
     src_inc,
   ],
+  c_args: libnm_systemd_common_cflags,
   dependencies: [
     libnm_systemd_shared_dep_inc,
     glib_dep,
diff --git a/src/libnm-systemd-core/sd-adapt-core/netif-util.c b/src/libnm-systemd-core/sd-adapt-core/netif-util.c
new file mode 100644
index 00000000..cfb361a3
--- /dev/null
+++ b/src/libnm-systemd-core/sd-adapt-core/netif-util.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-core.h"
+
+#include <linux/if.h>
+#ifdef __GLIBC__
+#include <linux/if_arp.h>
+#endif
+
+#include "arphrd-util.h"
+#include "device-util.h"
+#include "log-link.h"
+#include "memory-util.h"
+#include "netif-util.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "strv.h"
+
+#if 0  /* NM_IGNORED */
+bool netif_has_carrier(uint8_t operstate, unsigned flags) {
+        /* see Documentation/networking/operstates.txt in the kernel sources */
+
+        if (operstate == IF_OPER_UP)
+                return true;
+
+        if (operstate != IF_OPER_UNKNOWN)
+                return false;
+
+        /* operstate may not be implemented, so fall back to flags */
+        return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
+                !FLAGS_SET(flags, IFF_DORMANT);
+}
+
+int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
+        const char *t;
+        char *p;
+
+        if (device &&
+            sd_device_get_devtype(device, &t) >= 0 &&
+            !isempty(t)) {
+                p = strdup(t);
+                if (!p)
+                        return -ENOMEM;
+
+                *ret = p;
+                return 0;
+        }
+
+        t = arphrd_to_name(iftype);
+        if (!t)
+                return -ENOENT;
+
+        p = strdup(t);
+        if (!p)
+                return -ENOMEM;
+
+        *ret = ascii_strlower(p);
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+const char *
+net_get_persistent_name(sd_device *device)
+{
+    assert(device);
+
+    /* fetch some persistent data unique (on this machine) to this device */
+    FOREACH_STRING(field,
+                   "ID_NET_NAME_ONBOARD",
+                   "ID_NET_NAME_SLOT",
+                   "ID_NET_NAME_PATH",
+                   "ID_NET_NAME_MAC")
+    {
+        const char *name;
+
+        if (sd_device_get_property_value(device, field, &name) >= 0)
+            return name;
+    }
+
+    return NULL;
+}
+
+#if 0 /* NM_IGNORED */
+/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */
+#define HASH_KEY SD_ID128_MAKE(d3, 1e, 48, fa, 90, fe, 4b, 4c, 9d, af, d5, d7, a1, b1, 2e, 8a)
+
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) {
+        const char *name;
+
+        assert(device);
+        assert(ret);
+
+        /* net_get_persistent_name() will return one of the device names based on stable information about
+         * the device. If this is not available, we fall back to using the actual device name. */
+        name = net_get_persistent_name(device);
+        if (!name && use_sysname)
+                (void) sd_device_get_sysname(device, &name);
+        if (!name)
+                return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
+                                              "No stable identifying information found");
+
+        log_device_debug(device, "Using \"%s\" as stable identifying information", name);
+
+        return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret);
+}
+
+int net_get_unique_predictable_data_from_name(
+                const char *name,
+                const sd_id128_t *key,
+                uint64_t *ret) {
+
+        size_t l, sz;
+        uint8_t *v;
+        int r;
+
+        assert(name);
+        assert(key);
+        assert(ret);
+
+        l = strlen(name);
+        sz = sizeof(sd_id128_t) + l;
+        v = newa(uint8_t, sz);
+
+        /* Fetch some persistent data unique to this machine */
+        r = sd_id128_get_machine((sd_id128_t*) v);
+        if (r < 0)
+                 return r;
+
+        memcpy(v + sizeof(sd_id128_t), name, l);
+
+        /* Let's hash the machine ID plus the device name. We use
+         * a fixed, but originally randomly created hash key here. */
+        *ret = htole64(siphash24(v, sz, key->bytes));
+        return 0;
+}
+
+typedef struct Link {
+        const char *ifname;
+} Link;
+
+int net_verify_hardware_address(
+                const char *ifname,
+                bool is_static,
+                uint16_t iftype,
+                const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */
+                struct hw_addr_data *new_hw_addr) {
+
+        Link link = { .ifname = ifname };
+
+        assert(new_hw_addr);
+
+        if (new_hw_addr->length == 0)
+                return 0;
+
+        if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) {
+                if (is_static)
+                        log_link_warning(&link,
+                                         "Specified MAC address with invalid length (%zu, expected %zu), refusing.",
+                                         new_hw_addr->length, arphrd_to_hw_addr_len(iftype));
+                return -EINVAL;
+        }
+
+        switch (iftype) {
+        case ARPHRD_ETHER:
+                /* see eth_random_addr() in the kernel */
+
+                if (ether_addr_is_null(&new_hw_addr->ether)) {
+                        if (is_static)
+                                log_link_warning(&link, "Specified MAC address is null, refusing.");
+                        return -EINVAL;
+                }
+
+                if (ether_addr_is_broadcast(&new_hw_addr->ether)) {
+                        if (is_static)
+                                log_link_warning(&link, "Specified MAC address is broadcast, refusing.");
+                        return -EINVAL;
+                }
+
+                if (ether_addr_is_multicast(&new_hw_addr->ether)) {
+                        if (is_static)
+                                log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit.");
+
+                        new_hw_addr->bytes[0] &= 0xfe;
+                }
+
+                if (!is_static && !ether_addr_is_local(&new_hw_addr->ether))
+                        /* Adjust local assignment bit when the MAC address is generated randomly. */
+                        new_hw_addr->bytes[0] |= 0x02;
+
+                break;
+
+        case ARPHRD_INFINIBAND:
+                /* see ipoib_check_lladdr() in the kernel */
+
+                assert(ib_hw_addr);
+                assert(ib_hw_addr->length == INFINIBAND_ALEN);
+
+                if (is_static &&
+                    (!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) ||
+                     memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0))
+                        log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes.");
+
+                if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) {
+                        if (is_static)
+                                log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing.");
+                        return -EINVAL;
+                }
+
+                memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8);
+                break;
+
+        default:
+                if (is_static)
+                        log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.",
+                                         strna(arphrd_to_name(iftype)), iftype);
+                return -EINVAL;
+        }
+
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/sd-adapt-core/netif-util.h b/src/libnm-systemd-core/sd-adapt-core/netif-util.h
new file mode 100644
index 00000000..59a80866
--- /dev/null
+++ b/src/libnm-systemd-core/sd-adapt-core/netif-util.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sd-device.h"
+#include "sd-id128.h"
+
+#include "ether-addr-util.h"
+
+bool        netif_has_carrier(uint8_t operstate, unsigned flags);
+int         net_get_type_string(sd_device *device, uint16_t iftype, char **ret);
+const char *net_get_persistent_name(sd_device *device);
+int         net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret);
+int
+net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret);
+int net_verify_hardware_address(const char                *ifname,
+                                bool                       is_static,
+                                uint16_t                   iftype,
+                                const struct hw_addr_data *ib_hw_addr,
+                                struct hw_addr_data       *new_hw_addr);
diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c
index c5ef63eb..13effd12 100644
--- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c
+++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c
@@ -12,15 +12,6 @@
 
 /*****************************************************************************/
 
-int
-asynchronous_close(int fd)
-{
-    safe_close(fd);
-    return -1;
-}
-
-/*****************************************************************************/
-
 sd_device *
 sd_device_ref(sd_device *self)
 {
diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h
index 9c317801..9cb5574d 100644
--- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h
+++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h
@@ -86,7 +86,6 @@ sd_notify(int unset_environment, const char *state)
 
 #include "sd-id128.h"
 #include "sparse-endian.h"
-#include "async.h"
 
 #endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */
 
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
new file mode 100644
index 00000000..f8bc15c4
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-device.h"
+#include "sd-dhcp-duid.h"
+#include "sd-id128.h"
+
+#include "ether-addr-util.h"
+#include "macro.h"
+#include "sparse-endian.h"
+
+#define SYSTEMD_PEN    43793
+
+typedef enum DUIDType {
+        DUID_TYPE_LLT      = SD_DUID_TYPE_LLT,
+        DUID_TYPE_EN       = SD_DUID_TYPE_EN,
+        DUID_TYPE_LL       = SD_DUID_TYPE_LL,
+        DUID_TYPE_UUID     = SD_DUID_TYPE_UUID,
+        _DUID_TYPE_MAX,
+        _DUID_TYPE_INVALID = -EINVAL,
+} DUIDType;
+
+/* RFC 8415 section 11.1:
+ * A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
+ * octets that make up the actual identifier. The length of the DUID (not including the type code) is at
+ * least 1 octet and at most 128 octets. */
+#define MIN_DUID_DATA_LEN 1
+#define MAX_DUID_DATA_LEN 128
+#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
+#define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
+
+/* 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[];
+                } _packed_ llt;
+                struct {
+                        /* DUID_TYPE_EN */
+                        be32_t pen;
+                        uint8_t id[];
+                } _packed_ en;
+                struct {
+                        /* DUID_TYPE_LL */
+                        be16_t htype;
+                        uint8_t haddr[];
+                } _packed_ ll;
+                struct {
+                        /* DUID_TYPE_UUID */
+                        sd_id128_t uuid;
+                } _packed_ uuid;
+                uint8_t data[MAX_DUID_DATA_LEN];
+        };
+} _packed_;
+
+typedef struct sd_dhcp_duid {
+        size_t size;
+        union {
+                struct duid duid;
+                uint8_t raw[MAX_DUID_LEN];
+        };
+} sd_dhcp_duid;
+
+static inline bool duid_size_is_valid(size_t size) {
+        return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
+}
+
+static inline bool duid_data_size_is_valid(size_t size) {
+        return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
+}
+
+const char *duid_type_to_string(DUIDType t) _const_;
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
+
+int dhcp_identifier_set_iaid(
+                sd_device *dev,
+                const struct hw_addr_data *hw_addr,
+                bool legacy_unstable_byteorder,
+                void *ret);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
deleted file mode 100644
index 05d0585a..00000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
+++ /dev/null
@@ -1,252 +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 "dhcp-identifier.h"
-#include "netif-util.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-table.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 */
-
-static const char * const 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_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
-
-int dhcp_validate_duid_len(DUIDType 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 */
-static int dhcp_identifier_set_duid_llt(
-                const struct hw_addr_data *hw_addr,
-                uint16_t arp_type,
-                usec_t t,
-                struct duid *ret_duid,
-                size_t *ret_len) {
-
-        uint16_t time_from_2000y;
-
-        assert(hw_addr);
-        assert(ret_duid);
-        assert(ret_len);
-
-        if (hw_addr->length == 0)
-                return -EOPNOTSUPP;
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EOPNOTSUPP;
-
-        if (t < USEC_2000)
-                time_from_2000y = 0;
-        else
-                time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
-
-        unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
-        unaligned_write_be16(&ret_duid->llt.htype, arp_type);
-        unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
-        memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
-
-        *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
-
-        return 0;
-}
-
-static int dhcp_identifier_set_duid_ll(
-                const struct hw_addr_data *hw_addr,
-                uint16_t arp_type,
-                struct duid *ret_duid,
-                size_t *ret_len) {
-
-        assert(hw_addr);
-        assert(ret_duid);
-        assert(ret_len);
-
-        if (hw_addr->length == 0)
-                return -EOPNOTSUPP;
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EOPNOTSUPP;
-
-        unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
-        unaligned_write_be16(&ret_duid->ll.htype, arp_type);
-        memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
-
-        *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
-
-        return 0;
-}
-#endif /* NM_IGNORED */
-
-int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) {
-        sd_id128_t machine_id;
-        uint64_t hash;
-        int r;
-
-        assert(ret_duid);
-        assert(ret_len);
-
-        if (!test_mode) {
-                r = sd_id128_get_machine(&machine_id);
-                if (r < 0)
-                        return r;
-        } else
-                /* For tests, especially for fuzzers, reproducibility is important.
-                 * Hence, use a static and constant machine ID.
-                 * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
-                machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
-
-        unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
-        unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
-
-        /* 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(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
-
-        *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id);
-
-        if (test_mode)
-                assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
-
-        return 0;
-}
-
-#if 0 /* NM_IGNORED */
-static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
-        sd_id128_t machine_id;
-        int r;
-
-        assert(ret_duid);
-        assert(ret_len);
-
-        r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
-        if (r < 0)
-                return r;
-
-        unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
-        memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
-
-        *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
-
-        return 0;
-}
-
-int dhcp_identifier_set_duid(
-                DUIDType duid_type,
-                const struct hw_addr_data *hw_addr,
-                uint16_t arp_type,
-                usec_t llt_time,
-                bool test_mode,
-                struct duid *ret_duid,
-                size_t *ret_len) {
-
-        switch (duid_type) {
-        case DUID_TYPE_LLT:
-                return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
-        case DUID_TYPE_EN:
-                return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
-        case DUID_TYPE_LL:
-                return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
-        case DUID_TYPE_UUID:
-                return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
-        default:
-                return -EINVAL;
-        }
-}
-#endif /* NM_IGNORED */
-
-int dhcp_identifier_set_iaid(
-                sd_device *dev,
-                const struct hw_addr_data *hw_addr,
-                bool legacy_unstable_byteorder,
-                void *ret) {
-#if 0 /* NM_IGNORED */
-
-        const char *name = NULL;
-        uint32_t id32;
-        uint64_t id;
-
-        assert(hw_addr);
-        assert(ret);
-
-        if (dev)
-                name = net_get_persistent_name(dev);
-        if (name)
-                id = siphash24(name, strlen(name), HASH_KEY.bytes);
-        else
-                /* fall back to MAC address if no predictable name available */
-                id = siphash24(hw_addr->bytes, hw_addr->length, 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(ret, 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/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
deleted file mode 100644
index 523dfc4a..00000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-device.h"
-#include "sd-id128.h"
-
-#include "ether-addr-util.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  = -EINVAL,
-} 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(DUIDType duid_type, size_t duid_len, bool strict);
-int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid(
-                DUIDType duid_type,
-                const struct hw_addr_data *hw_addr,
-                uint16_t arp_type,
-                usec_t llt_time,
-                bool test_mode,
-                struct duid *ret_duid,
-                size_t *ret_len);
-int dhcp_identifier_set_iaid(
-                sd_device *dev,
-                const struct hw_addr_data *hw_addr,
-                bool legacy_unstable_byteorder,
-                void *ret);
-
-const char *duid_type_to_string(DUIDType t) _const_;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h
new file mode 100644
index 00000000..6c17f574
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp6-client.h"
+
+int dhcp6_client_set_state_callback(
+                sd_dhcp6_client *client,
+                sd_dhcp6_client_callback_t cb,
+                void *userdata);
+int dhcp6_client_get_state(sd_dhcp6_client *client);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
index fa43f28e..3fbfc028 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -11,7 +11,8 @@
 #include "sd-event.h"
 #include "sd-dhcp6-client.h"
 
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
+#include "dhcp6-client-internal.h"
 #include "dhcp6-option.h"
 #include "dhcp6-protocol.h"
 #include "ether-addr-util.h"
@@ -63,8 +64,7 @@ struct sd_dhcp6_client {
         DHCP6IA ia_na;
         DHCP6IA ia_pd;
         DHCP6RequestIA request_ia;
-        struct duid duid;
-        size_t duid_len;
+        sd_dhcp_duid duid;
         be16_t *req_opts;
         size_t n_req_opts;
         char *fqdn;
@@ -79,10 +79,9 @@ struct sd_dhcp6_client {
 
         sd_dhcp6_client_callback_t callback;
         void *userdata;
+        sd_dhcp6_client_callback_t state_callback;
+        void *state_userdata;
         bool send_release;
-
-        /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
-        bool test_mode;
 };
 
 int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
@@ -90,7 +89,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
                                   const void *packet, size_t len);
 
 int dhcp6_client_send_message(sd_dhcp6_client *client);
-void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
 
 #define log_dhcp6_client_errno(client, error, fmt, ...)         \
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
index 1f10dccb..e76a108f 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
@@ -10,7 +10,9 @@
 #include "sd-dhcp6-lease.h"
 
 #include "dhcp6-option.h"
+#include "dhcp6-protocol.h"
 #include "macro.h"
+#include "set.h"
 #include "time-util.h"
 
 struct sd_dhcp6_lease {
@@ -43,9 +45,11 @@ struct sd_dhcp6_lease {
         struct in6_addr *sntp;
         size_t sntp_count;
         char *fqdn;
+        char *captive_portal;
+        struct sd_dhcp6_option **sorted_vendor_options;
+        Set *vendor_options;
 };
 
-int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid);
 int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
 int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
 int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
@@ -60,6 +64,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
 int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const 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_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 
 int dhcp6_lease_new(sd_dhcp6_lease **ret);
 int dhcp6_lease_new_from_message(
@@ -69,3 +74,17 @@ int dhcp6_lease_new_from_message(
                 const triple_timestamp *timestamp,
                 const struct in6_addr *server_address,
                 sd_dhcp6_lease **ret);
+
+#define _FOREACH_DHCP6_ADDRESS(lease, it)                               \
+        for (int it = sd_dhcp6_lease_address_iterator_reset(lease);     \
+             it > 0;                                                    \
+             it = sd_dhcp6_lease_address_iterator_next(lease))
+#define FOREACH_DHCP6_ADDRESS(lease)                                    \
+        _FOREACH_DHCP6_ADDRESS(lease, UNIQ_T(i, UNIQ))
+
+#define _FOREACH_DHCP6_PD_PREFIX(lease, it)                             \
+        for (int it = sd_dhcp6_lease_pd_iterator_reset(lease);          \
+             it > 0;                                                    \
+             it = sd_dhcp6_lease_pd_iterator_next(lease))
+#define FOREACH_DHCP6_PD_PREFIX(lease)                                  \
+        _FOREACH_DHCP6_PD_PREFIX(lease, UNIQ_T(i, UNIQ))
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
index 23cf8a89..5fa9b265 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
@@ -17,6 +17,7 @@
 #include "dns-domain.h"
 #include "escape.h"
 #include "memory-util.h"
+#include "network-common.h"
 #include "strv.h"
 #include "unaligned.h"
 
@@ -526,6 +527,26 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
         return status;
 }
 
+/* parse a string from dhcp option field. *ret must be initialized */
+int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret) {
+        _cleanup_free_ char *string = NULL;
+        int r;
+
+        assert(data || data_len == 0);
+        assert(ret);
+
+        if (data_len <= 0) {
+                *ret = mfree(*ret);
+                return 0;
+        }
+
+        r = make_cstring((const char *) data, data_len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &string);
+        if (r < 0)
+                return r;
+
+        return free_and_replace(*ret, string);
+}
+
 static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
         int r;
 
@@ -567,7 +588,7 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
 
 static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
         _cleanup_free_ DHCP6Address *a = NULL;
-        uint32_t lt_valid, lt_pref;
+        usec_t lt_valid, lt_pref;
         int r;
 
         assert(ia);
@@ -586,17 +607,18 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
 
         memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
 
-        lt_valid = be32toh(a->iaaddr.lifetime_valid);
-        lt_pref = be32toh(a->iaaddr.lifetime_preferred);
+        lt_valid = be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true);
+        lt_pref = be32_sec_to_usec(a->iaaddr.lifetime_preferred, /* max_as_infinity = */ true);
 
         if (lt_valid == 0)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
                                               "Received an IA address with zero valid lifetime, ignoring.");
         if (lt_pref > lt_valid)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                              "Received an IA address with preferred lifetime %"PRIu32
-                                              " larger than valid lifetime %"PRIu32", ignoring.",
-                                              lt_pref, lt_valid);
+                                              "Received an IA address with preferred lifetime %s "
+                                              "larger than valid lifetime %s, ignoring.",
+                                              FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
+                                              FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
 
         if (len > sizeof(struct iaaddr)) {
                 r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
@@ -610,7 +632,7 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
 
 static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
         _cleanup_free_ DHCP6Address *a = NULL;
-        uint32_t lt_valid, lt_pref;
+        usec_t lt_valid, lt_pref;
         int r;
 
         assert(ia);
@@ -629,17 +651,18 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
 
         memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
 
-        lt_valid = be32toh(a->iapdprefix.lifetime_valid);
-        lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
+        lt_valid = be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true);
+        lt_pref = be32_sec_to_usec(a->iapdprefix.lifetime_preferred, /* max_as_infinity = */ true);
 
         if (lt_valid == 0)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
                                               "Received a PD prefix with zero valid lifetime, ignoring.");
         if (lt_pref > lt_valid)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                              "Received a PD prefix with preferred lifetime %"PRIu32
-                                              " larger than valid lifetime %"PRIu32", ignoring.",
-                                              lt_pref, lt_valid);
+                                              "Received a PD prefix with preferred lifetime %s "
+                                              "larger than valid lifetime %s, ignoring.",
+                                              FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
+                                              FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
 
         if (len > sizeof(struct iapdprefix)) {
                 r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
@@ -660,7 +683,7 @@ int dhcp6_option_parse_ia(
                 DHCP6IA **ret) {
 
         _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
-        uint32_t lt_t1, lt_t2;
+        usec_t lt_t1, lt_t2;
         size_t header_len;
         int r;
 
@@ -710,17 +733,18 @@ int dhcp6_option_parse_ia(
                                               "from the one chosen by the client, ignoring.");
 
         /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
-        lt_t1 = be32toh(ia->header.lifetime_t1);
-        lt_t2 = be32toh(ia->header.lifetime_t2);
+        lt_t1 = be32_sec_to_usec(ia->header.lifetime_t1, /* max_as_infinity = */ true);
+        lt_t2 = be32_sec_to_usec(ia->header.lifetime_t2, /* max_as_infinity = */ true);
 
         if (lt_t1 > lt_t2)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                              "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
-                                              lt_t1, lt_t2);
+                                              "Received an IA option with T1 %s > T2 %s, ignoring.",
+                                              FORMAT_TIMESPAN(lt_t1, USEC_PER_SEC),
+                                              FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
         if (lt_t1 == 0 && lt_t2 > 0)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                              "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.",
-                                              lt_t2);
+                                              "Received an IA option with zero T1 and non-zero T2 (%s), ignoring.",
+                                              FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
 
         for (size_t offset = header_len; offset < option_data_len;) {
                 const uint8_t *subdata;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
index 36841dd2..614b4f8a 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
@@ -88,6 +88,7 @@ int dhcp6_option_parse(
                 size_t *ret_option_data_len,
                 const uint8_t **ret_option_data);
 int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
+int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret);
 int dhcp6_option_parse_ia(
                 sd_dhcp6_client *client,
                 be32_t iaid,
diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.c b/src/libnm-systemd-core/src/libsystemd-network/network-common.c
index bbc28941..8d62e6ff 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/network-common.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.c
@@ -2,8 +2,11 @@
 
 #include "nm-sd-adapt-core.h"
 
+#include "env-util.h"
 #include "format-util.h"
 #include "network-common.h"
+#include "socket-util.h"
+#include "unaligned.h"
 
 int get_ifname(int ifindex, char **ifname) {
         assert(ifname);
@@ -15,3 +18,111 @@ int get_ifname(int ifindex, char **ifname) {
 
         return format_ifname_alloc(ifindex, ifname);
 }
+
+usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity) {
+        uint32_t s = unaligned_read_be32(ASSERT_PTR(p));
+
+        if (s == UINT32_MAX && max_as_infinity)
+                return USEC_INFINITY;
+
+        return s * USEC_PER_SEC;
+}
+
+usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity) {
+        uint32_t s = be32toh(t);
+
+        if (s == UINT32_MAX && max_as_infinity)
+                return USEC_INFINITY;
+
+        return s * USEC_PER_SEC;
+}
+
+usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity) {
+        uint32_t s = be32toh(t);
+
+        if (s == UINT32_MAX && max_as_infinity)
+                return USEC_INFINITY;
+
+        return s * USEC_PER_MSEC;
+}
+
+usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity) {
+        uint16_t s = be16toh(t);
+
+        if (s == UINT16_MAX && max_as_infinity)
+                return USEC_INFINITY;
+
+        return s * USEC_PER_SEC;
+}
+
+be32_t usec_to_be32_sec(usec_t t) {
+        if (t == USEC_INFINITY)
+                /* Some settings, e.g. a lifetime of an address, UINT32_MAX is handled as infinity. so let's
+                 * map USEC_INFINITY to UINT32_MAX. */
+                return htobe32(UINT32_MAX);
+
+        if (t >= (UINT32_MAX - 1) * USEC_PER_SEC)
+                /* Finite but too large. Let's use the largest (or off-by-one from the largest) finite value. */
+                return htobe32(UINT32_MAX - 1);
+
+        return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_SEC));
+}
+
+be32_t usec_to_be32_msec(usec_t t) {
+        if (t == USEC_INFINITY)
+                return htobe32(UINT32_MAX);
+
+        if (t >= (UINT32_MAX - 1) * USEC_PER_MSEC)
+                return htobe32(UINT32_MAX - 1);
+
+        return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_MSEC));
+}
+
+be16_t usec_to_be16_sec(usec_t t) {
+        if (t == USEC_INFINITY)
+                return htobe16(UINT16_MAX);
+
+        if (t >= (UINT16_MAX - 1) * USEC_PER_SEC)
+                return htobe16(UINT16_MAX - 1);
+
+        return htobe16((uint16_t) DIV_ROUND_UP(t, USEC_PER_SEC));
+}
+
+usec_t time_span_to_stamp(usec_t span, usec_t base) {
+        /* Typically, 0 lifetime (timespan) indicates the corresponding configuration (address or so) must be
+         * dropped. So, when the timespan is zero, here we return 0 rather than 'base'. This makes the caller
+         * easily understand that the configuration needs to be dropped immediately. */
+        if (span == 0)
+                return 0;
+
+        return usec_add(base, span);
+}
+
+bool network_test_mode_enabled(void) {
+        static int test_mode = -1;
+        int r;
+
+        if (test_mode < 0) {
+                r = getenv_bool("SYSTEMD_NETWORK_TEST_MODE");
+                if (r < 0) {
+                        if (r != -ENXIO)
+                                log_debug_errno(r, "Failed to parse $SYSTEMD_NETWORK_TEST_MODE environment variable, ignoring: %m");
+
+                        test_mode = false;
+                } else
+                        test_mode = r;
+        }
+
+        return test_mode;
+}
+
+triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh) {
+        assert(t);
+        assert(mh);
+
+        struct timeval *tv = CMSG_FIND_AND_COPY_DATA(mh, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
+        if (tv)
+                return triple_timestamp_from_realtime(t, timeval_load(tv));
+
+        return triple_timestamp_now(t);
+}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.h b/src/libnm-systemd-core/src/libsystemd-network/network-common.h
index 2b0e3b56..1750f181 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/network-common.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.h
@@ -1,7 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <sys/socket.h>
+
 #include "log-link.h"
+#include "sparse-endian.h"
+#include "time-util.h"
 
 #define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \
         ({                                                              \
@@ -28,3 +32,18 @@
         })
 
 int get_ifname(int ifindex, char **ifname);
+
+usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity);
+usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity);
+usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity);
+usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity);
+be32_t usec_to_be32_sec(usec_t t);
+be32_t usec_to_be32_msec(usec_t t);
+be16_t usec_to_be16_sec(usec_t t);
+usec_t time_span_to_stamp(usec_t span, usec_t base);
+
+bool network_test_mode_enabled(void);
+
+triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh);
+#define TRIPLE_TIMESTAMP_FROM_CMSG(mh)          \
+        triple_timestamp_from_cmsg(&(triple_timestamp) {}, mh)
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c
new file mode 100644
index 00000000..e664a4a7
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c
@@ -0,0 +1,290 @@
+/* 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 "dhcp-duid-internal.h"
+#include "hexdecoct.h"
+#include "netif-util.h"
+#include "network-common.h"
+#include "siphash24.h"
+#include "string-table.h"
+#include "unaligned.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 */
+
+static const char * const 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_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
+        assert_return(duid, -EINVAL);
+
+        *duid = (sd_dhcp_duid) {};
+        return 0;
+}
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
+        if (!duid)
+                return false;
+
+        return duid_size_is_valid(duid->size);
+}
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+        assert_return(ret_type, -EINVAL);
+        assert_return(ret_data, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        *ret_type = be16toh(duid->duid.type);
+        *ret_data = duid->duid.data;
+        *ret_size = duid->size - offsetof(struct duid, data);
+        return 0;
+}
+
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+        assert_return(ret_data, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        /* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
+
+        *ret_data = duid->raw;
+        *ret_size = duid->size;
+        return 0;
+}
+
+int sd_dhcp_duid_set(
+                sd_dhcp_duid *duid,
+                uint16_t duid_type,
+                const void *data,
+                size_t data_size) {
+
+        assert_return(duid, -EINVAL);
+        assert_return(data, -EINVAL);
+
+        if (!duid_data_size_is_valid(data_size))
+                return -EINVAL;
+
+        unaligned_write_be16(&duid->duid.type, duid_type);
+        memcpy(duid->duid.data, data, data_size);
+
+        duid->size = offsetof(struct duid, data) + data_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_raw(
+                sd_dhcp_duid *duid,
+                const void *data,
+                size_t data_size) {
+
+        assert_return(duid, -EINVAL);
+        assert_return(data, -EINVAL);
+
+        /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
+
+        if (!duid_size_is_valid(data_size))
+                return -EINVAL;
+
+        memcpy(duid->raw, data, data_size);
+
+        duid->size = data_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_llt(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type,
+                uint64_t usec) {
+
+        uint16_t time_from_2000y;
+
+        assert_return(duid, -EINVAL);
+        assert_return(hw_addr, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EOPNOTSUPP;
+
+        time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
+        unaligned_write_be16(&duid->duid.llt.htype, arp_type);
+        unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
+        memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
+
+        duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_ll(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type) {
+
+        assert_return(duid, -EINVAL);
+        assert_return(hw_addr, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EOPNOTSUPP;
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
+        unaligned_write_be16(&duid->duid.ll.htype, arp_type);
+        memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
+
+        duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
+        sd_id128_t machine_id;
+        bool test_mode;
+        uint64_t hash;
+        int r;
+
+        assert_return(duid, -EINVAL);
+
+        test_mode = network_test_mode_enabled();
+
+        if (!test_mode) {
+                r = sd_id128_get_machine(&machine_id);
+                if (r < 0)
+                        return r;
+        } else
+                /* For tests, especially for fuzzers, reproducibility is important.
+                 * Hence, use a static and constant machine ID.
+                 * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
+                machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
+        unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
+
+        /* 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->duid.en.id, &hash, sizeof(hash));
+
+        duid->size = offsetof(struct duid, en.id) + sizeof(hash);
+
+        if (test_mode)
+                assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
+
+        return 0;
+}
+
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
+        sd_id128_t machine_id;
+        int r;
+
+        assert_return(duid, -EINVAL);
+
+        r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
+        if (r < 0)
+                return r;
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
+        memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
+
+        duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
+        return 0;
+}
+
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
+        _cleanup_free_ char *p = NULL, *x = NULL;
+        const char *t;
+
+        assert(data);
+        assert(ret);
+
+        if (!duid_data_size_is_valid(data_size))
+                return -EINVAL;
+
+        x = hexmem(data, data_size);
+        if (!x)
+                return -ENOMEM;
+
+        t = duid_type_to_string(type);
+        if (!t)
+                return asprintf(ret, "%04x:%s", htobe16(type), x);
+
+        p = strjoin(t, ":", x);
+        if (!p)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(p);
+        return 0;
+}
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
+        uint16_t type;
+        const void *data;
+        size_t data_size;
+        int r;
+
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
+        if (r < 0)
+                return r;
+
+        return dhcp_duid_to_string_internal(type, data, data_size, ret);
+}
+
+int dhcp_identifier_set_iaid(
+                sd_device *dev,
+                const struct hw_addr_data *hw_addr,
+                bool legacy_unstable_byteorder,
+                void *ret) {
+
+        const char *name = NULL;
+        uint32_t id32;
+        uint64_t id;
+
+        assert(hw_addr);
+        assert(ret);
+
+        if (dev)
+                name = net_get_persistent_name(dev);
+        if (name)
+                id = siphash24(name, strlen(name), HASH_KEY.bytes);
+        else
+                /* fall back to MAC address if no predictable name available */
+                id = siphash24(hw_addr->bytes, hw_addr->length, 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(ret, id32);
+        return 0;
+}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
index 0e6f21f4..7c201164 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
@@ -7,27 +7,24 @@
 
 #include <errno.h>
 #include <sys/ioctl.h>
-#if 0 /* NM_IGNORED */
+#ifdef __GLIBC__
 #include <linux/if_arp.h>
-#else /* NM_IGNORED */
-#include <net/if_arp.h>
-#endif /* NM_IGNORED */
+#endif
 #include <linux/if_infiniband.h>
 
 #include "sd-dhcp6-client.h"
 
 #include "alloc-util.h"
 #include "device-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.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 "io-util.h"
+#include "iovec-util.h"
 #include "random-util.h"
 #include "socket-util.h"
 #include "sort-util.h"
@@ -52,6 +49,19 @@ int sd_dhcp6_client_set_callback(
         return 0;
 }
 
+int dhcp6_client_set_state_callback(
+                sd_dhcp6_client *client,
+                sd_dhcp6_client_callback_t cb,
+                void *userdata) {
+
+        assert_return(client, -EINVAL);
+
+        client->state_callback = cb;
+        client->state_userdata = userdata;
+
+        return 0;
+}
+
 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@@ -184,10 +194,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
 static int client_ensure_duid(sd_dhcp6_client *client) {
         assert(client);
 
-        if (client->duid_len != 0)
+        if (sd_dhcp_duid_is_set(&client->duid))
                 return 0;
 
-        return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
+        return sd_dhcp6_client_set_duid_en(client);
 }
 
 /**
@@ -195,101 +205,102 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
  * 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,
-                DUIDType duid_type,
-                const void *duid,
-                size_t duid_len,
-                usec_t llt_time) {
+int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
-        assert_return(duid_len == 0 || duid, -EINVAL);
 
-        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");
+        r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
 
-                        log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
-                }
+        return 0;
+}
 
-                client->duid.type = htobe16(duid_type);
-                memcpy(&client->duid.raw.data, duid, duid_len);
-                client->duid_len = sizeof(client->duid.type) + duid_len;
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
+        int r;
 
-        } else {
-#if 0 /* NM_IGNORED */
-                r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
-                                             client->test_mode, &client->duid, &client->duid_len);
-                if (r == -EOPNOTSUPP)
-                        return log_dhcp6_client_errno(client, r,
-                                                      "Failed to set %s. MAC address is not set or "
-                                                      "interface type is not supported.",
-                                                      duid_type_to_string(duid_type));
-                if (r < 0)
-                        return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
-                                                      duid_type_to_string(duid_type));
-#else /* NM_IGNORED */
-                g_return_val_if_reached (-EINVAL);
-#endif /* NM_IGNORED */
-        }
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
 
         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_en(sd_dhcp6_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        r = sd_dhcp_duid_set_en(&client->duid);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
+
+        return 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);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        r = sd_dhcp_duid_set_uuid(&client->duid);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
+
+        return 0;
 }
 
-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 sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
         int r;
 
         assert_return(client, -EINVAL);
-        assert_return(client->duid_len > 0, -ENODATA);
-        assert_return(duid, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+        assert_return(duid || duid_len == 0, -EINVAL);
 
-        v = 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;
-        }
+        r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
+        if (r < 0)
+                return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
 
-        t = hexmem(&client->duid.raw.data, client->duid_len);
-        if (!t)
-                return -ENOMEM;
+        return 0;
+}
 
-        p = strjoin(s, ":", t);
-        if (!p)
-                return -ENOMEM;
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
 
-        *duid = TAKE_PTR(p);
+        client->duid = *duid;
+        return 0;
+}
 
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!sd_dhcp_duid_is_set(&client->duid))
+                return -ENODATA;
+
+        *ret = &client->duid;
         return 0;
 }
 
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!sd_dhcp_duid_is_set(&client->duid))
+                return -ENODATA;
+
+        return sd_dhcp_duid_to_string(&client->duid, ret);
+}
+
 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@@ -335,12 +346,6 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
         return 0;
 }
 
-void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
-        assert(client);
-
-        client->test_mode = test_mode;
-}
-
 int sd_dhcp6_client_set_fqdn(
                 sd_dhcp6_client *client,
                 const char *fqdn) {
@@ -491,7 +496,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
 
 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
         assert(client);
-        assert(client->test_mode);
+        assert_se(network_test_mode_enabled());
 
         /* This is for tests or fuzzers. */
 
@@ -510,7 +515,6 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
 
 int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
         assert_return(client, -EINVAL);
-        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
 
         client->send_release = enable;
         return 0;
@@ -552,6 +556,15 @@ static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
                          dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
 
         client->state = state;
+
+        if (client->state_callback)
+                client->state_callback(client, state, client->state_userdata);
+}
+
+int dhcp6_client_get_state(sd_dhcp6_client *client) {
+        assert_return(client, -EINVAL);
+
+        return client->state;
 }
 
 static void client_notify(sd_dhcp6_client *client, int event) {
@@ -809,9 +822,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
         if (r < 0)
                 return r;
 
-        assert(client->duid_len > 0);
+        assert(sd_dhcp_duid_is_set(&client->duid));
         r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
-                                client->duid_len, &client->duid);
+                                client->duid.size, &client->duid.duid);
         if (r < 0)
                 return r;
 
@@ -824,7 +837,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
         /* RFC 8415 Section 21.9.
          * A client MUST include an Elapsed Time option in messages to indicate how long the client has
          * been trying to complete a DHCP message exchange. */
-        elapsed_usec = NM_MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
+        elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
         elapsed_time = htobe16(elapsed_usec);
         r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
         if (r < 0)
@@ -1047,12 +1060,20 @@ static int client_enter_bound_state(sd_dhcp6_client *client) {
         (void) event_source_disable(client->receive_message);
         (void) event_source_disable(client->timeout_resend);
 
-        r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid);
+        r = sd_dhcp6_lease_get_t1(client->lease, &lifetime_t1);
+        if (r < 0)
+                goto error;
+
+        r = sd_dhcp6_lease_get_t2(client->lease, &lifetime_t2);
+        if (r < 0)
+                goto error;
+
+        r = sd_dhcp6_lease_get_valid_lifetime(client->lease, &lifetime_valid);
         if (r < 0)
                 goto error;
 
         lifetime_t2 = client_timeout_compute_random(lifetime_t2);
-        lifetime_t1 = client_timeout_compute_random(NM_MIN(lifetime_t1, lifetime_t2));
+        lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
 
         if (lifetime_t1 == USEC_INFINITY) {
                 log_dhcp6_client(client, "Infinite T1");
@@ -1286,16 +1307,15 @@ static int client_receive_message(
                 .msg_control = &control,
                 .msg_controllen = sizeof(control),
         };
-        triple_timestamp t = {};
+        triple_timestamp t;
         _cleanup_free_ DHCP6Message *message = NULL;
         struct in6_addr *server_address = NULL;
         ssize_t buflen, len;
 
         buflen = next_datagram_size_fd(fd);
+        if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
+                return 0;
         if (buflen < 0) {
-                if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
-                        return 0;
-
                 log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
                 return 0;
         }
@@ -1307,10 +1327,9 @@ static int client_receive_message(
         iov = IOVEC_MAKE(message, buflen);
 
         len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
+        if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
+                return 0;
         if (len < 0) {
-                if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
-                        return 0;
-
                 log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
                 return 0;
         }
@@ -1329,9 +1348,7 @@ static int client_receive_message(
                 server_address = &sa.in6.sin6_addr;
         }
 
-        struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
-        if (tv)
-                triple_timestamp_from_realtime(&t, timeval_load(tv));
+        triple_timestamp_from_cmsg(&t, &msg);
 
         if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
                 return 0;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
index 3e25b4e8..a42df243 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
@@ -10,7 +10,9 @@
 #include "alloc-util.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
+#include "network-common.h"
 #include "strv.h"
+#include "unaligned.h"
 
 #define IRT_DEFAULT (1 * USEC_PER_DAY)
 #define IRT_MINIMUM (600 * USEC_PER_SEC)
@@ -21,7 +23,7 @@ static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timest
         if (timestamp && triple_timestamp_is_set(timestamp))
                 lease->timestamp = *timestamp;
         else
-                triple_timestamp_get(&lease->timestamp);
+                triple_timestamp_now(&lease->timestamp);
 }
 
 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
@@ -37,30 +39,26 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
         return 0;
 }
 
-static usec_t sec2usec(uint32_t sec) {
-        return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
-}
-
 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
-        uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
+        usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
 
         assert(lease);
         assert(lease->ia_na || lease->ia_pd);
 
         if (lease->ia_na) {
-                t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
-                t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
+                t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true));
+                t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true));
 
                 LIST_FOREACH(addresses, a, lease->ia_na->addresses)
-                        min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
+                        min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true));
         }
 
         if (lease->ia_pd) {
-                t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
-                t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
+                t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true));
+                t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true));
 
                 LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
-                        min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
+                        min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
         }
 
         if (t2 == 0 || t2 > min_valid_lt) {
@@ -70,25 +68,52 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
                 t2 = min_valid_lt / 10 * 8;
         }
 
-        lease->lifetime_valid = sec2usec(min_valid_lt);
-        lease->lifetime_t1 = sec2usec(t1);
-        lease->lifetime_t2 = sec2usec(t2);
-}
-
-int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
-        assert(lease);
+        lease->lifetime_valid = min_valid_lt;
+        lease->lifetime_t1 = t1;
+        lease->lifetime_t2 = t2;
+}
+
+#define DEFINE_GET_TIME_FUNCTIONS(name, val)                            \
+        int sd_dhcp6_lease_get_##name(                                  \
+                        sd_dhcp6_lease *lease,                          \
+                        uint64_t *ret) {                                \
+                                                                        \
+                assert_return(lease, -EINVAL);                          \
+                                                                        \
+                if (!lease->ia_na && !lease->ia_pd)                     \
+                        return -ENODATA;                                \
+                                                                        \
+                if (ret)                                                \
+                        *ret = lease->val;                              \
+                return 0;                                               \
+        }                                                               \
+                                                                        \
+        int sd_dhcp6_lease_get_##name##_timestamp(                      \
+                        sd_dhcp6_lease *lease,                          \
+                        clockid_t clock,                                \
+                        uint64_t *ret) {                                \
+                                                                        \
+                usec_t s, t;                                            \
+                int r;                                                  \
+                                                                        \
+                assert_return(lease, -EINVAL);                          \
+                                                                        \
+                r = sd_dhcp6_lease_get_##name(lease, &s);               \
+                if (r < 0)                                              \
+                        return r;                                       \
+                                                                        \
+                r = sd_dhcp6_lease_get_timestamp(lease, clock, &t);     \
+                if (r < 0)                                              \
+                        return r;                                       \
+                                                                        \
+                if (ret)                                                \
+                        *ret = time_span_to_stamp(s, t);                \
+                return 0;                                               \
+        }
 
-        if (!lease->ia_na && !lease->ia_pd)
-                return -ENODATA;
-
-        if (ret_t1)
-                *ret_t1 = lease->lifetime_t1;
-        if (ret_t2)
-                *ret_t2 = lease->lifetime_t2;
-        if (ret_valid)
-                *ret_valid = lease->lifetime_valid;
-        return 0;
-}
+DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1);
+DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t1);
+DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid);
 
 static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
         assert(lease);
@@ -218,61 +243,151 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
         return 0;
 }
 
-int sd_dhcp6_lease_get_address(
+int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
+        assert_return(lease, -EINVAL);
+
+        if (!lease->addr_iter)
+                return -ENODATA;
+
+        if (ret)
+                *ret = lease->addr_iter->iaaddr.address;
+        return 0;
+}
+
+int sd_dhcp6_lease_get_address_lifetime(
                 sd_dhcp6_lease *lease,
-                struct in6_addr *ret_addr,
-                uint32_t *ret_lifetime_preferred,
-                uint32_t *ret_lifetime_valid) {
+                usec_t *ret_lifetime_preferred,
+                usec_t *ret_lifetime_valid) {
+
+        const struct iaaddr *a;
 
         assert_return(lease, -EINVAL);
 
         if (!lease->addr_iter)
                 return -ENODATA;
 
-        if (ret_addr)
-                *ret_addr = lease->addr_iter->iaaddr.address;
+        a = &lease->addr_iter->iaaddr;
+
         if (ret_lifetime_preferred)
-                *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
+                *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
         if (ret_lifetime_valid)
-                *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
+                *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
+        return 0;
+}
+
+int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) {
+        if (!lease)
+                return false;
+
+        lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
+        return !!lease->addr_iter;
+}
+
+int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) {
+        if (!lease || !lease->addr_iter)
+                return false;
 
         lease->addr_iter = lease->addr_iter->addresses_next;
-        return 0;
+        return !!lease->addr_iter;
 }
 
-void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
-        if (lease)
-                lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
+int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) {
+        return lease && lease->ia_na;
 }
 
-int sd_dhcp6_lease_get_pd(
+int sd_dhcp6_lease_get_pd_prefix(
                 sd_dhcp6_lease *lease,
                 struct in6_addr *ret_prefix,
-                uint8_t *ret_prefix_len,
-                uint32_t *ret_lifetime_preferred,
-                uint32_t *ret_lifetime_valid) {
+                uint8_t *ret_prefix_len) {
+
+        const struct iapdprefix *a;
 
         assert_return(lease, -EINVAL);
 
         if (!lease->prefix_iter)
                 return -ENODATA;
 
+        a = &lease->prefix_iter->iapdprefix;
+
         if (ret_prefix)
-                *ret_prefix = lease->prefix_iter->iapdprefix.address;
+                *ret_prefix = a->address;
         if (ret_prefix_len)
-                *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
+                *ret_prefix_len = a->prefixlen;
+        return 0;
+}
+
+int sd_dhcp6_lease_get_pd_lifetime(
+                sd_dhcp6_lease *lease,
+                uint64_t *ret_lifetime_preferred,
+                uint64_t *ret_lifetime_valid) {
+
+        const struct iapdprefix *a;
+
+        assert_return(lease, -EINVAL);
+
+        if (!lease->prefix_iter)
+                return -ENODATA;
+
+        a = &lease->prefix_iter->iapdprefix;
+
         if (ret_lifetime_preferred)
-                *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
+                *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
         if (ret_lifetime_valid)
-                *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
-
-        lease->prefix_iter = lease->prefix_iter->addresses_next;
+                *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
         return 0;
 }
 
-void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
-        if (lease)
-                lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
+int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) {
+        if (!lease)
+                return false;
+
+        lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
+        return !!lease->prefix_iter;
+}
+
+int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) {
+        if (!lease || !lease->prefix_iter)
+                return false;
+
+        lease->prefix_iter = lease->prefix_iter->addresses_next;
+        return !!lease->prefix_iter;
+}
+
+#define DEFINE_GET_TIMESTAMP2(name)                                     \
+        int sd_dhcp6_lease_get_##name##_lifetime_timestamp(             \
+                        sd_dhcp6_lease *lease,                          \
+                        clockid_t clock,                                \
+                        uint64_t *ret_lifetime_preferred,               \
+                        uint64_t *ret_lifetime_valid) {                 \
+                                                                        \
+                usec_t t, p, v;                                         \
+                int r;                                                  \
+                                                                        \
+                assert_return(lease, -EINVAL);                          \
+                                                                        \
+                r = sd_dhcp6_lease_get_##name##_lifetime(               \
+                                lease,                                  \
+                                ret_lifetime_preferred ? &p : NULL,     \
+                                ret_lifetime_valid ? &v : NULL);        \
+                if (r < 0)                                              \
+                        return r;                                       \
+                                                                        \
+                r = sd_dhcp6_lease_get_timestamp(lease, clock, &t);     \
+                if (r < 0)                                              \
+                        return r;                                       \
+                                                                        \
+                if (ret_lifetime_preferred)                             \
+                        *ret_lifetime_preferred = time_span_to_stamp(p, t); \
+                if (ret_lifetime_valid)                                 \
+                        *ret_lifetime_valid = time_span_to_stamp(v, t); \
+                return 0;                                               \
+        }
+
+DEFINE_GET_TIMESTAMP2(address);
+DEFINE_GET_TIMESTAMP2(pd);
+
+int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
+        return lease && lease->ia_pd;
 }
 
 int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
@@ -447,6 +562,111 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
         return 0;
 }
 
+int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+        _cleanup_free_ char *uri = NULL;
+        int r;
+
+        assert(lease);
+        assert(optval || optlen == 0);
+
+        r = dhcp6_option_parse_string(optval, optlen, &uri);
+        if (r < 0)
+                return r;
+
+        if (uri && !in_charset(uri, URI_VALID))
+                return -EINVAL;
+
+        return free_and_replace(lease->captive_portal, uri);
+}
+
+int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
+        assert_return(lease, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!lease->captive_portal)
+                return -ENODATA;
+
+        *ret = lease->captive_portal;
+        return 0;
+}
+
+int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
+        int r;
+
+        assert_return(lease, -EINVAL);
+
+        if (set_isempty(lease->vendor_options))
+                return -ENODATA;
+
+        if (ret) {
+                if (!lease->sorted_vendor_options) {
+                        r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
+                        if (r < 0)
+                                return r;
+                }
+
+                *ret = lease->sorted_vendor_options;
+        }
+
+        return set_size(lease->vendor_options);
+}
+
+static int dhcp6_lease_insert_vendor_option(
+                sd_dhcp6_lease *lease,
+                uint16_t option_code,
+                const void *data,
+                size_t len,
+                uint32_t enterprise_id) {
+
+        _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
+
+        assert(lease);
+
+        option = new(sd_dhcp6_option, 1);
+        if (!option)
+                return -ENOMEM;
+
+        *option = (sd_dhcp6_option) {
+                .n_ref = 1,
+                .enterprise_identifier = enterprise_id,
+                .option = option_code,
+                .length = len,
+        };
+        option->data = memdup_suffix0(data, len);
+        if (!option->data)
+                return -ENOMEM;
+
+        return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
+}
+
+static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+        int r;
+        uint32_t enterprise_id;
+
+        assert(lease);
+        assert(optval || optlen == 0);
+
+        if (optlen < sizeof(be32_t))
+                return -EBADMSG;
+
+        enterprise_id = unaligned_read_be32(optval);
+
+        for (size_t offset = 4; offset < optlen;) {
+                const uint8_t *subval;
+                size_t sublen;
+                uint16_t subopt;
+
+                r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
+                if (r < 0)
+                        return r;
+
+                r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
+                if (r < 0)
+                        return r;
+        }
+        return 0;
+}
+
 static int dhcp6_lease_parse_message(
                 sd_dhcp6_client *client,
                 sd_dhcp6_lease *lease,
@@ -467,6 +687,11 @@ static int dhcp6_lease_parse_message(
                 size_t optlen;
                 const uint8_t *optval;
 
+                if (len - offset < offsetof(DHCP6Option, data)) {
+                        log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
+                        break;
+                }
+
                 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
                 if (r < 0)
                         return log_dhcp6_client_errno(client, r,
@@ -607,6 +832,12 @@ static int dhcp6_lease_parse_message(
 
                         break;
 
+                case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
+                        r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
+                        if (r < 0)
+                                log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
+                        break;
+
                 case SD_DHCP6_OPTION_CLIENT_FQDN:
                         r = dhcp6_lease_set_fqdn(lease, optval, optlen);
                         if (r < 0)
@@ -619,7 +850,14 @@ static int dhcp6_lease_parse_message(
                                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
                                                               "Received information refresh time option with an invalid length (%zu).", optlen);
 
-                        irt = unaligned_read_be32(optval) * USEC_PER_SEC;
+                        irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
+                        break;
+
+                case SD_DHCP6_OPTION_VENDOR_OPTS:
+                        r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
+                        if (r < 0)
+                                log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
+
                         break;
                 }
         }
@@ -631,7 +869,7 @@ static int dhcp6_lease_parse_message(
                                               "%s message does not contain client ID. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
 
-        if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
+        if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
                                               "The client ID in %s message does not match. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
@@ -661,12 +899,15 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         if (!lease)
                 return NULL;
 
+        set_free(lease->vendor_options);
+        free(lease->sorted_vendor_options);
         free(lease->clientid);
         free(lease->serverid);
         dhcp6_ia_free(lease->ia_na);
         dhcp6_ia_free(lease->ia_pd);
         free(lease->dns);
         free(lease->fqdn);
+        free(lease->captive_portal);
         strv_free(lease->domains);
         free(lease->ntp);
         strv_free(lease->ntp_fqdn);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
new file mode 100644
index 00000000..a465eb25
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-device.h"
+
+#include "device-private.h"
+#include "hashmap.h"
+#include "set.h"
+#include "time-util.h"
+
+#define LATEST_UDEV_DATABASE_VERSION 1
+
+struct sd_device {
+        unsigned n_ref;
+
+        /* The database version indicates the supported features by the udev database.
+         * This is saved and parsed in V field.
+         *
+         * 0: None of the following features are supported (systemd version <= 246).
+         * 1: The current tags (Q) and the database version (V) features are implemented (>= 247).
+         */
+        unsigned database_version;
+
+        sd_device *parent;
+
+        OrderedHashmap *properties;
+        Iterator properties_iterator;
+        uint64_t properties_generation; /* changes whenever the properties are changed */
+        uint64_t properties_iterator_generation; /* generation when iteration was started */
+
+        /* the subset of the properties that should be written to the db */
+        OrderedHashmap *properties_db;
+
+        Hashmap *sysattr_values; /* cached sysattr values */
+
+        Set *sysattrs; /* names of sysattrs */
+        Iterator sysattrs_iterator;
+
+        Set *all_tags, *current_tags;
+        Iterator all_tags_iterator, current_tags_iterator;
+        uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */
+        uint64_t tags_generation; /* changes whenever the tags are changed */
+
+        Set *devlinks;
+        Iterator devlinks_iterator;
+        uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
+        uint64_t devlinks_iterator_generation; /* generation when iteration was started */
+        int devlink_priority;
+
+        Hashmap *children;
+        Iterator children_iterator;
+        bool children_enumerated;
+
+        int ifindex;
+        char *devtype;
+        char *devname;
+        dev_t devnum;
+
+        char **properties_strv; /* the properties hashmap as a strv */
+        char *properties_nulstr; /* the same as a nulstr */
+        size_t properties_nulstr_len;
+
+        char *syspath;
+        const char *devpath;
+        const char *sysnum;
+        char *sysname;
+
+        char *subsystem;
+        char *driver_subsystem; /* only set for the 'drivers' subsystem */
+        char *driver;
+
+        char *device_id;
+
+        usec_t usec_initialized;
+
+        mode_t devmode;
+        uid_t devuid;
+        gid_t devgid;
+
+        uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
+
+        /* only set when device is passed through netlink */
+        sd_device_action_t action;
+        uint64_t seqnum;
+
+        bool parent_set:1; /* no need to try to reload parent */
+        bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
+        bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */
+        bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */
+        bool properties_buf_outdated:1; /* need to reread hashmap */
+        bool subsystem_set:1; /* don't reread subsystem */
+        bool driver_set:1; /* don't reread driver */
+        bool uevent_loaded:1; /* don't reread uevent */
+        bool db_loaded; /* don't reread db */
+
+        bool is_initialized:1;
+        bool sealed:1; /* don't read more information from uevent/db */
+        bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
+};
+
+int device_new_aux(sd_device **ret);
+int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db);
+static inline int device_add_property_internal(sd_device *device, const char *key, const char *value) {
+        return device_add_property_aux(device, key, value, false);
+}
+
+int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
+int device_set_ifindex(sd_device *device, const char *ifindex);
+int device_set_devmode(sd_device *device, const char *devmode);
+int device_set_devname(sd_device *device, const char *devname);
+int device_set_devtype(sd_device *device, const char *devtype);
+int device_set_devnum(sd_device *device, const char *major, const char *minor);
+int device_set_subsystem(sd_device *device, const char *subsystem);
+int device_set_diskseq(sd_device *device, const char *str);
+int device_set_drivers_subsystem(sd_device *device);
+int device_set_driver(sd_device *device, const char *driver);
+int device_set_usec_initialized(sd_device *device, usec_t when);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
new file mode 100644
index 00000000..eef3d618
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
@@ -0,0 +1,962 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-core.h"
+
+#include <ctype.h>
+#include <net/if.h>
+#include <sys/types.h>
+
+#include "sd-device.h"
+
+#include "alloc-util.h"
+#include "device-internal.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "nulstr-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "tmpfile-util.h"
+#include "user-util.h"
+
+#if 0 /* NM_IGNORED */
+int device_add_property(sd_device *device, const char *key, const char *value) {
+        int r;
+
+        assert(device);
+        assert(key);
+
+        r = device_add_property_aux(device, key, value, false);
+        if (r < 0)
+                return r;
+
+        if (key[0] != '.') {
+                r = device_add_property_aux(device, key, value, true);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) {
+        _cleanup_free_ char *value = NULL;
+        va_list ap;
+        int r;
+
+        assert(device);
+        assert(key);
+
+        if (!format)
+                return device_add_property(device, key, NULL);
+
+        va_start(ap, format);
+        r = vasprintf(&value, format, ap);
+        va_end(ap);
+
+        if (r < 0)
+                return -ENOMEM;
+
+        return device_add_property(device, key, value);
+}
+
+void device_set_devlink_priority(sd_device *device, int priority) {
+        assert(device);
+
+        device->devlink_priority = priority;
+}
+
+void device_set_is_initialized(sd_device *device) {
+        assert(device);
+
+        device->is_initialized = true;
+}
+
+int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
+        usec_t when;
+
+        assert(device);
+
+        if (device_old && device_old->usec_initialized > 0)
+                when = device_old->usec_initialized;
+        else
+                when = now(CLOCK_MONOTONIC);
+
+        return device_set_usec_initialized(device, when);
+}
+
+uint64_t device_get_properties_generation(sd_device *device) {
+        assert(device);
+
+        return device->properties_generation;
+}
+
+uint64_t device_get_tags_generation(sd_device *device) {
+        assert(device);
+
+        return device->tags_generation;
+}
+
+uint64_t device_get_devlinks_generation(sd_device *device) {
+        assert(device);
+
+        return device->devlinks_generation;
+}
+
+int device_get_devnode_mode(sd_device *device, mode_t *ret) {
+        int r;
+
+        assert(device);
+
+        r = device_read_db(device);
+        if (r < 0)
+                return r;
+
+        if (device->devmode == MODE_INVALID)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->devmode;
+
+        return 0;
+}
+
+int device_get_devnode_uid(sd_device *device, uid_t *ret) {
+        int r;
+
+        assert(device);
+
+        r = device_read_db(device);
+        if (r < 0)
+                return r;
+
+        if (device->devuid == UID_INVALID)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->devuid;
+
+        return 0;
+}
+
+static int device_set_devuid(sd_device *device, const char *uid) {
+        uid_t u;
+        int r;
+
+        assert(device);
+        assert(uid);
+
+        r = parse_uid(uid, &u);
+        if (r < 0)
+                return r;
+
+        r = device_add_property_internal(device, "DEVUID", uid);
+        if (r < 0)
+                return r;
+
+        device->devuid = u;
+
+        return 0;
+}
+
+int device_get_devnode_gid(sd_device *device, gid_t *ret) {
+        int r;
+
+        assert(device);
+
+        r = device_read_db(device);
+        if (r < 0)
+                return r;
+
+        if (device->devgid == GID_INVALID)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->devgid;
+
+        return 0;
+}
+
+static int device_set_devgid(sd_device *device, const char *gid) {
+        gid_t g;
+        int r;
+
+        assert(device);
+        assert(gid);
+
+        r = parse_gid(gid, &g);
+        if (r < 0)
+                return r;
+
+        r = device_add_property_internal(device, "DEVGID", gid);
+        if (r < 0)
+                return r;
+
+        device->devgid = g;
+
+        return 0;
+}
+
+int device_set_action(sd_device *device, sd_device_action_t a) {
+        int r;
+
+        assert(device);
+        assert(a >= 0 && a < _SD_DEVICE_ACTION_MAX);
+
+        r = device_add_property_internal(device, "ACTION", device_action_to_string(a));
+        if (r < 0)
+                return r;
+
+        device->action = a;
+
+        return 0;
+}
+
+static int device_set_action_from_string(sd_device *device, const char *action) {
+        sd_device_action_t a;
+
+        assert(device);
+        assert(action);
+
+        a = device_action_from_string(action);
+        if (a < 0)
+                return a;
+
+        return device_set_action(device, a);
+}
+
+static int device_set_seqnum(sd_device *device, const char *str) {
+        uint64_t seqnum;
+        int r;
+
+        assert(device);
+        assert(str);
+
+        r = safe_atou64(str, &seqnum);
+        if (r < 0)
+                return r;
+        if (seqnum == 0)
+                return -EINVAL;
+
+        r = device_add_property_internal(device, "SEQNUM", str);
+        if (r < 0)
+                return r;
+
+        device->seqnum = seqnum;
+
+        return 0;
+}
+
+static int device_amend(sd_device *device, const char *key, const char *value) {
+        int r;
+
+        assert(device);
+        assert(key);
+        assert(value);
+
+        if (streq(key, "DEVPATH")) {
+                char *path;
+
+                path = strjoina("/sys", value);
+
+                /* the caller must verify or trust this data (e.g., if it comes from the kernel) */
+                r = device_set_syspath(device, path, false);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path);
+        } else if (streq(key, "SUBSYSTEM")) {
+                r = device_set_subsystem(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value);
+        } else if (streq(key, "DEVTYPE")) {
+                r = device_set_devtype(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value);
+        } else if (streq(key, "DEVNAME")) {
+                r = device_set_devname(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value);
+        } else if (streq(key, "USEC_INITIALIZED")) {
+                usec_t t;
+
+                r = safe_atou64(value, &t);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value);
+
+                r = device_set_usec_initialized(device, t);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value);
+        } else if (streq(key, "DRIVER")) {
+                r = device_set_driver(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value);
+        } else if (streq(key, "IFINDEX")) {
+                r = device_set_ifindex(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value);
+        } else if (streq(key, "DEVMODE")) {
+                r = device_set_devmode(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value);
+        } else if (streq(key, "DEVUID")) {
+                r = device_set_devuid(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value);
+        } else if (streq(key, "DEVGID")) {
+                r = device_set_devgid(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value);
+        } else if (streq(key, "ACTION")) {
+                r = device_set_action_from_string(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set action to '%s': %m", value);
+        } else if (streq(key, "SEQNUM")) {
+                r = device_set_seqnum(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value);
+        } else if (streq(key, "DISKSEQ")) {
+                r = device_set_diskseq(device, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set DISKSEQ to '%s': %m", value);
+        } else if (streq(key, "DEVLINKS")) {
+                for (const char *p = value;;) {
+                        _cleanup_free_ char *word = NULL;
+
+                        /* udev rules may set escaped strings, and sd-device does not modify the input
+                         * strings. So, it is also necessary to keep the strings received through
+                         * sd-device-monitor. */
+                        r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                break;
+
+                        r = device_add_devlink(device, word);
+                        if (r < 0)
+                                return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", word);
+                }
+        } else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) {
+                for (const char *p = value;;) {
+                        _cleanup_free_ char *word = NULL;
+
+                        r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                break;
+                        if (isempty(word))
+                                continue;
+
+                        r = device_add_tag(device, word, streq(key, "CURRENT_TAGS"));
+                        if (r < 0)
+                                return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", word);
+                }
+        } else if (streq(key, "UDEV_DATABASE_VERSION")) {
+                r = safe_atou(value, &device->database_version);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to parse udev database version '%s': %m", value);
+        } else {
+                r = device_add_property_internal(device, key, value);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value);
+        }
+
+        return 0;
+}
+
+static int device_append(
+                sd_device *device,
+                char *key,
+                const char **_major,
+                const char **_minor) {
+
+        const char *major = NULL, *minor = NULL;
+        char *value;
+        int r;
+
+        assert(device);
+        assert(key);
+        assert(_major);
+        assert(_minor);
+
+        value = strchr(key, '=');
+        if (!value)
+                return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                              "sd-device: Not a key-value pair: '%s'", key);
+
+        *value = '\0';
+
+        value++;
+
+        if (streq(key, "MAJOR"))
+                major = value;
+        else if (streq(key, "MINOR"))
+                minor = value;
+        else {
+                r = device_amend(device, key, value);
+                if (r < 0)
+                        return r;
+        }
+
+        if (major)
+                *_major = major;
+
+        if (minor)
+                *_minor = minor;
+
+        return 0;
+}
+
+void device_seal(sd_device *device) {
+        assert(device);
+
+        device->sealed = true;
+}
+
+static int device_verify(sd_device *device) {
+        int r;
+
+        assert(device);
+
+        if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0)
+                return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                              "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
+
+        if (streq(device->subsystem, "drivers")) {
+                r = device_set_drivers_subsystem(device);
+                if (r < 0)
+                        return r;
+        }
+
+        device->sealed = true;
+
+        return 0;
+}
+
+int device_new_from_strv(sd_device **ret, char **strv) {
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        const char *major = NULL, *minor = NULL;
+        int r;
+
+        assert(ret);
+        assert(strv);
+
+        r = device_new_aux(&device);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(key, strv) {
+                r = device_append(device, *key, &major, &minor);
+                if (r < 0)
+                        return r;
+        }
+
+        if (major) {
+                r = device_set_devnum(device, major, minor);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
+        }
+
+        r = device_verify(device);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(device);
+
+        return 0;
+}
+
+int device_new_from_nulstr(sd_device **ret, char *nulstr, size_t len) {
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        const char *major = NULL, *minor = NULL;
+        int r;
+
+        assert(ret);
+        assert(nulstr);
+        assert(len);
+
+        r = device_new_aux(&device);
+        if (r < 0)
+                return r;
+
+        for (size_t i = 0; i < len; ) {
+                char *key;
+                const char *end;
+
+                key = nulstr + i;
+                end = memchr(key, '\0', len - i);
+                if (!end)
+                        return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                                      "sd-device: Failed to parse nulstr");
+
+                i += end - key + 1;
+
+                /* netlink messages for some devices contain an unwanted newline at the end of value.
+                 * Let's drop the newline and remaining characters after the newline. */
+                truncate_nl(key);
+
+                r = device_append(device, key, &major, &minor);
+                if (r < 0)
+                        return r;
+        }
+
+        if (major) {
+                r = device_set_devnum(device, major, minor);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
+        }
+
+        r = device_verify(device);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(device);
+
+        return 0;
+}
+
+static int device_update_properties_bufs(sd_device *device) {
+        _cleanup_free_ char **buf_strv = NULL, *buf_nulstr = NULL;
+        size_t nulstr_len = 0, num = 0;
+
+        assert(device);
+
+        if (!device->properties_buf_outdated)
+                return 0;
+
+        /* append udev database version */
+        buf_nulstr = newdup(char, "UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\0",
+                            STRLEN("UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION)) + 2);
+        if (!buf_nulstr)
+                return -ENOMEM;
+
+        nulstr_len += STRLEN("UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION)) + 1;
+        num++;
+
+        FOREACH_DEVICE_PROPERTY(device, prop, val) {
+                size_t len = 0;
+
+                len = strlen(prop) + 1 + strlen(val);
+
+                buf_nulstr = GREEDY_REALLOC0(buf_nulstr, nulstr_len + len + 2);
+                if (!buf_nulstr)
+                        return -ENOMEM;
+
+                strscpyl(buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
+                nulstr_len += len + 1;
+                num++;
+        }
+
+        /* build buf_strv from buf_nulstr */
+        buf_strv = new0(char*, num + 1);
+        if (!buf_strv)
+                return -ENOMEM;
+
+        size_t i = 0;
+        NULSTR_FOREACH(p, buf_nulstr)
+                buf_strv[i++] = p;
+        assert(i == num);
+
+        free_and_replace(device->properties_nulstr, buf_nulstr);
+        device->properties_nulstr_len = nulstr_len;
+        free_and_replace(device->properties_strv, buf_strv);
+
+        device->properties_buf_outdated = false;
+        return 0;
+}
+
+int device_get_properties_nulstr(sd_device *device, const char **ret_nulstr, size_t *ret_len) {
+        int r;
+
+        assert(device);
+
+        r = device_update_properties_bufs(device);
+        if (r < 0)
+                return r;
+
+        if (ret_nulstr)
+                *ret_nulstr = device->properties_nulstr;
+        if (ret_len)
+                *ret_len = device->properties_nulstr_len;
+
+        return 0;
+}
+
+int device_get_properties_strv(sd_device *device, char ***ret) {
+        int r;
+
+        assert(device);
+
+        r = device_update_properties_bufs(device);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = device->properties_strv;
+
+        return 0;
+}
+
+int device_get_devlink_priority(sd_device *device, int *ret) {
+        int r;
+
+        assert(device);
+
+        r = device_read_db(device);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = device->devlink_priority;
+
+        return 0;
+}
+
+int device_clone_with_db(sd_device *device, sd_device **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *dest = NULL;
+        const char *key, *val;
+        int r;
+
+        assert(device);
+        assert(ret);
+
+        /* The device may be already removed. Let's copy minimal set of information that was obtained through
+         * netlink socket. */
+
+        r = device_new_aux(&dest);
+        if (r < 0)
+                return r;
+
+        /* Seal device to prevent reading the uevent file, as the device may have been already removed. */
+        dest->sealed = true;
+
+        /* Copy syspath, then also devname, sysname or sysnum can be obtained. */
+        r = device_set_syspath(dest, device->syspath, false);
+        if (r < 0)
+                return r;
+
+        /* Copy other information stored in database. Here, do not use FOREACH_DEVICE_PROPERTY() and
+         * sd_device_get_property_value(), as they calls device_properties_prepare() ->
+         * device_read_uevent_file(), but as commented in the above, the device may be already removed and
+         * reading uevent file may fail. */
+        ORDERED_HASHMAP_FOREACH_KEY(val, key, device->properties) {
+                if (streq(key, "MINOR"))
+                        continue;
+
+                if (streq(key, "MAJOR")) {
+                        const char *minor = NULL;
+
+                        minor = ordered_hashmap_get(device->properties, "MINOR");
+                        r = device_set_devnum(dest, val, minor);
+                } else
+                        r = device_amend(dest, key, val);
+                if (r < 0)
+                        return r;
+
+                if (streq(key, "SUBSYSTEM") && streq(val, "drivers")) {
+                        r = free_and_strdup(&dest->driver_subsystem, device->driver_subsystem);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        /* Finally, read the udev database. */
+        r = device_read_db_internal(dest, /* force = */ true);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(dest);
+        return 0;
+}
+
+void device_cleanup_tags(sd_device *device) {
+        assert(device);
+
+        device->all_tags = set_free_free(device->all_tags);
+        device->current_tags = set_free_free(device->current_tags);
+        device->property_tags_outdated = true;
+        device->tags_generation++;
+}
+
+void device_cleanup_devlinks(sd_device *device) {
+        assert(device);
+
+        set_free_free(device->devlinks);
+        device->devlinks = NULL;
+        device->property_devlinks_outdated = true;
+        device->devlinks_generation++;
+}
+
+void device_remove_tag(sd_device *device, const char *tag) {
+        assert(device);
+        assert(tag);
+
+        free(set_remove(device->current_tags, tag));
+        device->property_tags_outdated = true;
+        device->tags_generation++;
+}
+
+static int device_tag(sd_device *device, const char *tag, bool add) {
+        const char *id;
+        char *path;
+        int r;
+
+        assert(device);
+        assert(tag);
+
+        r = device_get_device_id(device, &id);
+        if (r < 0)
+                return r;
+
+        path = strjoina("/run/udev/tags/", tag, "/", id);
+
+        if (add)
+                return touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
+
+        if (unlink(path) < 0 && errno != ENOENT)
+                return -errno;
+
+        return 0;
+}
+
+int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
+        int r = 0, k;
+
+        if (add && device_old)
+                /* delete possible left-over tags */
+                FOREACH_DEVICE_TAG(device_old, tag)
+                        if (!sd_device_has_tag(device, tag)) {
+                                k = device_tag(device_old, tag, false);
+                                if (r >= 0 && k < 0)
+                                        r = k;
+                        }
+
+        FOREACH_DEVICE_TAG(device, tag) {
+                k = device_tag(device, tag, add);
+                if (r >= 0 && k < 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+static bool device_has_info(sd_device *device) {
+        assert(device);
+
+        if (!set_isempty(device->devlinks))
+                return true;
+
+        if (device->devlink_priority != 0)
+                return true;
+
+        if (!ordered_hashmap_isempty(device->properties_db))
+                return true;
+
+        if (!set_isempty(device->all_tags))
+                return true;
+
+        if (!set_isempty(device->current_tags))
+                return true;
+
+        return false;
+}
+
+bool device_should_have_db(sd_device *device) {
+        assert(device);
+
+        if (device_has_info(device))
+                return true;
+
+        if (major(device->devnum) != 0)
+                return true;
+
+        if (device->ifindex != 0)
+                return true;
+
+        return false;
+}
+
+void device_set_db_persist(sd_device *device) {
+        assert(device);
+
+        device->db_persist = true;
+}
+#endif /* NM_IGNORED */
+
+static int device_get_db_path(sd_device *device, char **ret) {
+        const char *id;
+        char *path;
+        int r;
+
+        assert(device);
+        assert(ret);
+
+        r = device_get_device_id(device, &id);
+        if (r < 0)
+                return r;
+
+        path = path_join("/run/udev/data/", id);
+        if (!path)
+                return -ENOMEM;
+
+        *ret = path;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int device_has_db(sd_device *device) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        assert(device);
+
+        r = device_get_db_path(device, &path);
+        if (r < 0)
+                return r;
+
+        return access(path, F_OK) >= 0;
+}
+
+int device_update_db(sd_device *device) {
+        _cleanup_(unlink_and_freep) char *path = NULL, *path_tmp = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(device);
+
+        /* do not store anything for otherwise empty devices */
+        if (!device_should_have_db(device))
+                return device_delete_db(device);
+
+        r = device_get_db_path(device, &path);
+        if (r < 0)
+                return r;
+
+        /* write a database file */
+        r = mkdir_parents(path, 0755);
+        if (r < 0)
+                return log_device_debug_errno(device, r,
+                                              "sd-device: Failed to create parent directories of '%s': %m",
+                                              path);
+
+        r = fopen_temporary(path, &f, &path_tmp);
+        if (r < 0)
+                return log_device_debug_errno(device, r,
+                                              "sd-device: Failed to create temporary file for database file '%s': %m",
+                                              path);
+
+        /* set 'sticky' bit to indicate that we should not clean the database when we transition from initrd
+         * to the real root */
+        if (fchmod(fileno(f), device->db_persist ? 01644 : 0644) < 0)
+                return log_device_debug_errno(device, errno,
+                                              "sd-device: Failed to chmod temporary database file '%s': %m",
+                                              path_tmp);
+
+        if (device_has_info(device)) {
+                const char *property, *value, *ct;
+
+                if (major(device->devnum) > 0) {
+                        FOREACH_DEVICE_DEVLINK(device, devlink)
+                                fprintf(f, "S:%s\n", devlink + STRLEN("/dev/"));
+
+                        if (device->devlink_priority != 0)
+                                fprintf(f, "L:%i\n", device->devlink_priority);
+                }
+
+                if (device->usec_initialized > 0)
+                        fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized);
+
+                ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db)
+                        fprintf(f, "E:%s=%s\n", property, value);
+
+                FOREACH_DEVICE_TAG(device, tag)
+                        fprintf(f, "G:%s\n", tag); /* Any tag */
+
+                SET_FOREACH(ct, device->current_tags)
+                        fprintf(f, "Q:%s\n", ct); /* Current tag */
+
+                /* Always write the latest database version here, instead of the value stored in
+                 * device->database_version, as which may be 0. */
+                fputs("V:" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\n", f);
+        }
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return log_device_debug_errno(device, r,
+                                              "sd-device: Failed to flush temporary database file '%s': %m",
+                                              path_tmp);
+
+        if (rename(path_tmp, path) < 0)
+                return log_device_debug_errno(device, errno,
+                                              "sd-device: Failed to rename temporary database file '%s' to '%s': %m",
+                                              path_tmp, path);
+
+        log_device_debug(device, "sd-device: Created database file '%s' for '%s'.", path, device->devpath);
+
+        path_tmp = mfree(path_tmp);
+        path = mfree(path);
+
+        return 0;
+}
+
+int device_delete_db(sd_device *device) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        assert(device);
+
+        r = device_get_db_path(device, &path);
+        if (r < 0)
+                return r;
+
+        if (unlink(path) < 0 && errno != ENOENT)
+                return -errno;
+
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+int device_read_db_internal(sd_device *device, bool force) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        assert(device);
+
+        if (device->db_loaded || (!force && device->sealed))
+                return 0;
+
+        r = device_get_db_path(device, &path);
+        if (r < 0)
+                return r;
+
+        return device_read_db_internal_filename(device, path);
+}
+
+#if 0 /* NM_IGNORED */
+static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = {
+        [SD_DEVICE_ADD]     = "add",
+        [SD_DEVICE_REMOVE]  = "remove",
+        [SD_DEVICE_CHANGE]  = "change",
+        [SD_DEVICE_MOVE]    = "move",
+        [SD_DEVICE_ONLINE]  = "online",
+        [SD_DEVICE_OFFLINE] = "offline",
+        [SD_DEVICE_BIND]    = "bind",
+        [SD_DEVICE_UNBIND]  = "unbind",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(device_action, sd_device_action_t);
+
+void dump_device_action_table(void) {
+        DUMP_STRING_TABLE(device_action, sd_device_action_t, _SD_DEVICE_ACTION_MAX);
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
new file mode 100644
index 00000000..e1f3b6e8
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "sd-device.h"
+
+#include "macro.h"
+
+int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum);
+int device_new_from_nulstr(sd_device **ret, char *nulstr, size_t len);
+int device_new_from_strv(sd_device **ret, char **strv);
+
+int device_opendir(sd_device *device, const char *subdir, DIR **ret);
+
+int device_get_property_bool(sd_device *device, const char *key);
+int device_get_property_int(sd_device *device, const char *key, int *ret);
+int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
+int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
+int device_get_sysattr_bool(sd_device *device, const char *sysattr);
+int device_get_device_id(sd_device *device, const char **ret);
+int device_get_devlink_priority(sd_device *device, int *ret);
+int device_get_devnode_mode(sd_device *device, mode_t *ret);
+int device_get_devnode_uid(sd_device *device, uid_t *ret);
+int device_get_devnode_gid(sd_device *device, gid_t *ret);
+
+void device_clear_sysattr_cache(sd_device *device);
+int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
+int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
+
+void device_seal(sd_device *device);
+void device_set_is_initialized(sd_device *device);
+void device_set_db_persist(sd_device *device);
+void device_set_devlink_priority(sd_device *device, int priority);
+int device_ensure_usec_initialized(sd_device *device, sd_device *device_old);
+int device_add_devlink(sd_device *device, const char *devlink);
+int device_remove_devlink(sd_device *device, const char *devlink);
+bool device_has_devlink(sd_device *device, const char *devlink);
+int device_add_property(sd_device *device, const char *property, const char *value);
+int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4);
+int device_add_tag(sd_device *device, const char *tag, bool both);
+void device_remove_tag(sd_device *device, const char *tag);
+void device_cleanup_tags(sd_device *device);
+void device_cleanup_devlinks(sd_device *device);
+
+uint64_t device_get_properties_generation(sd_device *device);
+uint64_t device_get_tags_generation(sd_device *device);
+uint64_t device_get_devlinks_generation(sd_device *device);
+
+int device_properties_prepare(sd_device *device);
+int device_get_properties_nulstr(sd_device *device, const char **ret_nulstr, size_t *ret_len);
+int device_get_properties_strv(sd_device *device, char ***ret);
+
+int device_clone_with_db(sd_device *device, sd_device **ret);
+
+int device_tag_index(sd_device *dev, sd_device *dev_old, bool add);
+bool device_should_have_db(sd_device *device);
+int device_has_db(sd_device *device);
+int device_update_db(sd_device *device);
+int device_delete_db(sd_device *device);
+int device_read_db_internal_filename(sd_device *device, const char *filename); /* For fuzzer */
+int device_read_db_internal(sd_device *device, bool force);
+static inline int device_read_db(sd_device *device) {
+        return device_read_db_internal(device, false);
+}
+
+int device_read_uevent_file(sd_device *device);
+
+int device_set_action(sd_device *device, sd_device_action_t a);
+sd_device_action_t device_action_from_string(const char *s) _pure_;
+const char *device_action_to_string(sd_device_action_t a) _const_;
+void dump_device_action_table(void);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
index a1b5e91e..534a2967 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
@@ -7,39 +7,41 @@
 
 #include "sd-device.h"
 
+#include "alloc-util.h"
 #include "log.h"
 #include "macro.h"
+#include "strv.h"
 
 #define device_unref_and_replace(a, b)                                  \
         unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
 
-#define FOREACH_DEVICE_PROPERTY(device, key, value)                \
-        for (key = sd_device_get_property_first(device, &(value)); \
-             key;                                                  \
-             key = sd_device_get_property_next(device, &(value)))
+#define FOREACH_DEVICE_PROPERTY(device, key, value)                     \
+        for (const char *value, *key = sd_device_get_property_first(device, &value); \
+             key;                                                       \
+             key = sd_device_get_property_next(device, &value))
 
-#define FOREACH_DEVICE_TAG(device, tag)             \
-        for (tag = sd_device_get_tag_first(device); \
-             tag;                                   \
+#define FOREACH_DEVICE_TAG(device, tag)                                 \
+        for (const char *tag = sd_device_get_tag_first(device);         \
+             tag;                                                       \
              tag = sd_device_get_tag_next(device))
 
-#define FOREACH_DEVICE_CURRENT_TAG(device, tag)             \
-        for (tag = sd_device_get_current_tag_first(device); \
-             tag;                                   \
+#define FOREACH_DEVICE_CURRENT_TAG(device, tag)                         \
+        for (const char *tag = sd_device_get_current_tag_first(device); \
+             tag;                                                       \
              tag = sd_device_get_current_tag_next(device))
 
-#define FOREACH_DEVICE_SYSATTR(device, attr)             \
-        for (attr = sd_device_get_sysattr_first(device); \
-             attr;                                       \
+#define FOREACH_DEVICE_SYSATTR(device, attr)                            \
+        for (const char *attr = sd_device_get_sysattr_first(device);    \
+             attr;                                                      \
              attr = sd_device_get_sysattr_next(device))
 
-#define FOREACH_DEVICE_DEVLINK(device, devlink)             \
-        for (devlink = sd_device_get_devlink_first(device); \
-             devlink;                                   \
+#define FOREACH_DEVICE_DEVLINK(device, devlink)                         \
+        for (const char *devlink = sd_device_get_devlink_first(device); \
+             devlink;                                                   \
              devlink = sd_device_get_devlink_next(device))
 
 #define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr)                \
-        for (child = sd_device_get_child_first(device, suffix_ptr);     \
+        for (sd_device *child = sd_device_get_child_first(device, suffix_ptr); \
              child;                                                     \
              child = sd_device_get_child_next(device, suffix_ptr))
 
@@ -49,14 +51,14 @@
 #define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix)         \
         _FOREACH_DEVICE_CHILD(device, child, &suffix)
 
-#define FOREACH_DEVICE(enumerator, device)                               \
-        for (device = sd_device_enumerator_get_device_first(enumerator); \
-             device;                                                     \
+#define FOREACH_DEVICE(enumerator, device)                              \
+        for (sd_device *device = sd_device_enumerator_get_device_first(enumerator); \
+             device;                                                    \
              device = sd_device_enumerator_get_device_next(enumerator))
 
-#define FOREACH_SUBSYSTEM(enumerator, device)                               \
-        for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
-             device;                                                        \
+#define FOREACH_SUBSYSTEM(enumerator, device)                           \
+        for (sd_device *device = sd_device_enumerator_get_subsystem_first(enumerator); \
+             device;                                                    \
              device = sd_device_enumerator_get_subsystem_next(enumerator))
 
 #define log_device_full_errno_zerook(device, level, error, ...)         \
@@ -81,17 +83,17 @@
 
 #define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__)
 
-#define log_device_debug(device, ...)   log_device_full(device, LOG_DEBUG, __VA_ARGS__)
-#define log_device_info(device, ...)    log_device_full(device, LOG_INFO, __VA_ARGS__)
-#define log_device_notice(device, ...)  log_device_full(device, LOG_NOTICE, __VA_ARGS__)
+#define log_device_debug(device, ...)   log_device_full(device, LOG_DEBUG,   __VA_ARGS__)
+#define log_device_info(device, ...)    log_device_full(device, LOG_INFO,    __VA_ARGS__)
+#define log_device_notice(device, ...)  log_device_full(device, LOG_NOTICE,  __VA_ARGS__)
 #define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
-#define log_device_error(device, ...)   log_device_full(device, LOG_ERR, __VA_ARGS__)
+#define log_device_error(device, ...)   log_device_full(device, LOG_ERR,     __VA_ARGS__)
 
-#define log_device_debug_errno(device, error, ...)   log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
-#define log_device_info_errno(device, error, ...)    log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
-#define log_device_notice_errno(device, error, ...)  log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
+#define log_device_debug_errno(device, error, ...)   log_device_full_errno(device, LOG_DEBUG,   error, __VA_ARGS__)
+#define log_device_info_errno(device, error, ...)    log_device_full_errno(device, LOG_INFO,    error, __VA_ARGS__)
+#define log_device_notice_errno(device, error, ...)  log_device_full_errno(device, LOG_NOTICE,  error, __VA_ARGS__)
 #define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
-#define log_device_error_errno(device, error, ...)   log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
+#define log_device_error_errno(device, error, ...)   log_device_full_errno(device, LOG_ERR,     error, __VA_ARGS__)
 
 int devname_from_devnum(mode_t mode, dev_t devnum, char **ret);
 static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
@@ -101,3 +103,13 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
 int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
 
 char** device_make_log_fields(sd_device *device);
+
+bool device_in_subsystem(sd_device *device, const char *subsystem);
+bool device_is_devtype(sd_device *device, const char *devtype);
+
+static inline bool device_property_can_set(const char *property) {
+        return property &&
+                !STR_IN_SET(property,
+                            "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
+                            "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
+}
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
new file mode 100644
index 00000000..0ca87146
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
@@ -0,0 +1,2724 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-core.h"
+
+#include <ctype.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "sd-device.h"
+
+#include "alloc-util.h"
+#include "chase.h"
+#include "device-internal.h"
+#include "device-private.h"
+#include "device-util.h"
+#include "devnum-util.h"
+#include "dirent-util.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#if 0 /* NM_IGNORED */
+#include "id128-util.h"
+#endif /* NM_IGNORED */
+#include "macro.h"
+#include "missing_magic.h"
+#if 0 /* NM_IGNORED */
+#include "netlink-util.h"
+#endif /* NM_IGNORED */
+#include "parse-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "user-util.h"
+
+#if 0 /* NM_IGNORED */
+int device_new_aux(sd_device **ret) {
+        sd_device *device;
+
+        assert(ret);
+
+        device = new(sd_device, 1);
+        if (!device)
+                return -ENOMEM;
+
+        *device = (sd_device) {
+                .n_ref = 1,
+                .devmode = MODE_INVALID,
+                .devuid = UID_INVALID,
+                .devgid = GID_INVALID,
+                .action = _SD_DEVICE_ACTION_INVALID,
+        };
+
+        *ret = device;
+        return 0;
+}
+
+static sd_device *device_free(sd_device *device) {
+        assert(device);
+
+        sd_device_unref(device->parent);
+        free(device->syspath);
+        free(device->sysname);
+        free(device->devtype);
+        free(device->devname);
+        free(device->subsystem);
+        free(device->driver_subsystem);
+        free(device->driver);
+        free(device->device_id);
+        free(device->properties_strv);
+        free(device->properties_nulstr);
+
+        ordered_hashmap_free(device->properties);
+        ordered_hashmap_free(device->properties_db);
+        hashmap_free(device->sysattr_values);
+        set_free(device->sysattrs);
+        set_free(device->all_tags);
+        set_free(device->current_tags);
+        set_free(device->devlinks);
+        hashmap_free(device->children);
+
+        return mfree(device);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device, sd_device, device_free);
+#endif /* NM_IGNORED */
+
+int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db) {
+        OrderedHashmap **properties;
+
+        assert(device);
+        assert(key);
+
+        if (db)
+                properties = &device->properties_db;
+        else
+                properties = &device->properties;
+
+        if (value) {
+                _unused_ _cleanup_free_ char *old_value = NULL;
+                _cleanup_free_ char *new_key = NULL, *new_value = NULL, *old_key = NULL;
+                int r;
+
+                r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops_free_free);
+                if (r < 0)
+                        return r;
+
+                new_key = strdup(key);
+                if (!new_key)
+                        return -ENOMEM;
+
+                new_value = strdup(value);
+                if (!new_value)
+                        return -ENOMEM;
+
+                old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key);
+
+                /* ordered_hashmap_replace() does not fail when the hashmap already has the entry. */
+                r = ordered_hashmap_replace(*properties, new_key, new_value);
+                if (r < 0)
+                        return r;
+
+                TAKE_PTR(new_key);
+                TAKE_PTR(new_value);
+        } else {
+                _unused_ _cleanup_free_ char *old_value = NULL;
+                _cleanup_free_ char *old_key = NULL;
+
+                old_value = ordered_hashmap_remove2(*properties, key, (void**) &old_key);
+        }
+
+        if (!db) {
+                device->properties_generation++;
+                device->properties_buf_outdated = true;
+        }
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
+        _cleanup_free_ char *syspath = NULL;
+        const char *devpath;
+        int r;
+
+        assert(device);
+        assert(_syspath);
+
+        if (verify) {
+                _cleanup_close_ int fd = -EBADF;
+
+                /* The input path maybe a symlink located outside of /sys. Let's try to chase the symlink at first.
+                 * The primary use case is that e.g. /proc/device-tree is a symlink to /sys/firmware/devicetree/base.
+                 * By chasing symlinks in the path at first, we can call sd_device_new_from_path() with such path. */
+                r = chase(_syspath, NULL, 0, &syspath, &fd);
+                if (r == -ENOENT)
+                         /* the device does not exist (any more?) */
+                        return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                               "sd-device: Failed to chase symlinks in \"%s\".", _syspath);
+                if (r < 0)
+                        return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
+
+                if (!path_startswith(syspath, "/sys")) {
+                        _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL;
+                        char *p;
+
+                        /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */
+                        r = chase("/sys", NULL, 0, &real_sys, NULL);
+                        if (r < 0)
+                                return log_debug_errno(r, "sd-device: Failed to chase symlink /sys: %m");
+
+                        p = path_startswith(syspath, real_sys);
+                        if (!p)
+                                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                       "sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'",
+                                                       syspath, real_sys);
+
+                        new_syspath = path_join("/sys", p);
+                        if (!new_syspath)
+                                return log_oom_debug();
+
+                        free_and_replace(syspath, new_syspath);
+                        path_simplify(syspath);
+                }
+
+                if (path_startswith(syspath, "/sys/devices/")) {
+                        /* For proper devices, stricter rules apply: they must have a 'uevent' file,
+                         * otherwise we won't allow them */
+
+                        if (faccessat(fd, "uevent", F_OK, 0) < 0) {
+                                if (errno == ENOENT)
+                                        /* This is not a valid device.  Note, this condition is quite often
+                                         * satisfied when enumerating devices or finding a parent device.
+                                         * Hence, use log_trace_errno() here. */
+                                        return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                               "sd-device: the uevent file \"%s/uevent\" does not exist.", syspath);
+                                if (errno == ENOTDIR)
+                                        /* Not actually a directory. */
+                                        return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                               "sd-device: the syspath \"%s\" is not a directory.", syspath);
+
+                                return log_debug_errno(errno, "sd-device: cannot find uevent file for %s: %m", syspath);
+                        }
+                } else {
+                        struct stat st;
+
+                        /* For everything else lax rules apply: they just need to be a directory */
+
+                        if (fstat(fd, &st) < 0)
+                                return log_debug_errno(errno, "sd-device: failed to check if syspath \"%s\" is a directory: %m", syspath);
+                        if (!S_ISDIR(st.st_mode))
+                                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                       "sd-device: the syspath \"%s\" is not a directory.", syspath);
+                }
+
+                /* Only operate on sysfs, i.e. refuse going down into /sys/fs/cgroup/ or similar places where
+                 * things are not arranged as kobjects in kernel, and hence don't necessarily have
+                 * kobject/attribute structure. */
+                r = secure_getenv_bool("SYSTEMD_DEVICE_VERIFY_SYSFS");
+                if (r < 0 && r != -ENXIO)
+                        log_debug_errno(r, "Failed to parse $SYSTEMD_DEVICE_VERIFY_SYSFS value: %m");
+                if (r != 0) {
+                        r = fd_is_fs_type(fd, SYSFS_MAGIC);
+                        if (r < 0)
+                                return log_debug_errno(r, "sd-device: failed to check if syspath \"%s\" is backed by sysfs.", syspath);
+                        if (r == 0)
+                                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                       "sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath);
+                }
+        } else {
+                /* must be a subdirectory of /sys */
+                if (!path_startswith(_syspath, "/sys/"))
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "sd-device: Syspath '%s' is not a subdirectory of /sys",
+                                               _syspath);
+
+                r = path_simplify_alloc(_syspath, &syspath);
+                if (r < 0)
+                        return r;
+        }
+
+        assert_se(devpath = startswith(syspath, "/sys"));
+        if (devpath[0] != '/')
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path.");
+
+        r = device_add_property_internal(device, "DEVPATH", devpath);
+        if (r < 0)
+                return log_debug_errno(r, "sd-device: Failed to add \"DEVPATH\" property for device \"%s\": %m", syspath);
+
+        free_and_replace(device->syspath, syspath);
+        device->devpath = devpath;
+
+        /* Unset sysname and sysnum, they will be assigned when requested. */
+        device->sysnum = NULL;
+        device->sysname = mfree(device->sysname);
+        return 0;
+}
+
+static int device_new_from_syspath(sd_device **ret, const char *syspath, bool strict) {
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(syspath, -EINVAL);
+
+        if (strict && !path_startswith(syspath, "/sys/"))
+                return -EINVAL;
+
+        r = device_new_aux(&device);
+        if (r < 0)
+                return r;
+
+        r = device_set_syspath(device, syspath, /* verify= */ true);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(device);
+        return 0;
+}
+
+_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
+        return device_new_from_syspath(ret, syspath, /* strict = */ true);
+}
+
+int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        _cleanup_free_ char *syspath = NULL;
+        const char *t;
+        dev_t n;
+        int r;
+
+        assert(ret);
+
+        if (S_ISCHR(mode))
+                t = "char";
+        else if (S_ISBLK(mode))
+                t = "block";
+        else
+                return -ENOTTY;
+
+        if (major(devnum) == 0)
+                return -ENODEV;
+
+        if (asprintf(&syspath, "/sys/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
+                return -ENOMEM;
+
+        r = sd_device_new_from_syspath(&dev, syspath);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_devnum(dev, &n);
+        if (r == -ENOENT)
+                return -ENXIO;
+        if (r < 0)
+                return r;
+        if (n != devnum)
+                return -ENXIO;
+
+        if (device_in_subsystem(dev, "block") != !!S_ISBLK(mode))
+                return -ENXIO;
+
+        *ret = TAKE_PTR(dev);
+        return 0;
+}
+
+_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
+        assert_return(ret, -EINVAL);
+        assert_return(IN_SET(type, 'b', 'c'), -EINVAL);
+
+        return device_new_from_mode_and_devnum(ret, type == 'b' ? S_IFBLK : S_IFCHR, devnum);
+}
+
+static int device_new_from_main_ifname(sd_device **ret, const char *ifname) {
+        const char *syspath;
+
+        assert(ret);
+        assert(ifname);
+
+        syspath = strjoina("/sys/class/net/", ifname);
+        return sd_device_new_from_syspath(ret, syspath);
+}
+
+_public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) {
+        _cleanup_free_ char *main_name = NULL;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(ifname, -EINVAL);
+
+        r = device_new_from_main_ifname(ret, ifname);
+        if (r >= 0)
+                return r;
+
+        r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL);
+        if (r < 0)
+                return r;
+
+        return device_new_from_main_ifname(ret, main_name);
+}
+
+_public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        _cleanup_free_ char *ifname = NULL;
+        int r, i;
+
+        assert_return(ret, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
+
+        r = rtnl_get_ifname_full(NULL, ifindex, &ifname, NULL);
+        if (r < 0)
+                return r;
+
+        r = device_new_from_main_ifname(&dev, ifname);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_ifindex(dev, &i);
+        if (r == -ENOENT)
+                return -ENXIO;
+        if (r < 0)
+                return r;
+        if (i != ifindex)
+                return -ENXIO;
+
+        *ret = TAKE_PTR(dev);
+        return 0;
+}
+
+static int device_strjoin_new(
+                const char *a,
+                const char *b,
+                const char *c,
+                const char *d,
+                sd_device **ret) {
+
+        const char *p;
+        int r;
+
+        p = strjoina(a, b, c, d);
+        if (access(p, F_OK) < 0)
+                return IN_SET(errno, ENOENT, ENAMETOOLONG) ? 0 : -errno; /* If this sysfs is too long then it doesn't exist either */
+
+        r = sd_device_new_from_syspath(ret, p);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+_public_ int sd_device_new_from_subsystem_sysname(
+                sd_device **ret,
+                const char *subsystem,
+                const char *sysname) {
+
+        char *name;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(subsystem, -EINVAL);
+        assert_return(sysname, -EINVAL);
+
+        if (!path_is_normalized(subsystem))
+                return -EINVAL;
+        if (!path_is_normalized(sysname))
+                return -EINVAL;
+
+        /* translate sysname back to sysfs filename */
+        name = strdupa_safe(sysname);
+        string_replace_char(name, '/', '!');
+
+        if (streq(subsystem, "subsystem")) {
+                FOREACH_STRING(s, "/sys/bus/", "/sys/class/") {
+                        r = device_strjoin_new(s, name, NULL, NULL, ret);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                return 0;
+                }
+
+        } else if (streq(subsystem, "module")) {
+                r = device_strjoin_new("/sys/module/", name, NULL, NULL, ret);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0;
+
+        } else if (streq(subsystem, "drivers")) {
+                const char *sep;
+
+                sep = strchr(name, ':');
+                if (sep && sep[1] != '\0') { /* Require ":" and something non-empty after that. */
+
+                        const char *subsys = memdupa_suffix0(name, sep - name);
+                        sep++;
+
+                        if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
+                                r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret);
+                        else
+                                r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                return 0;
+                }
+        }
+
+        r = device_strjoin_new("/sys/bus/", subsystem, "/devices/", name, ret);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return 0;
+
+        r = device_strjoin_new("/sys/class/", subsystem, "/", name, ret);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return 0;
+
+        r = device_strjoin_new("/sys/firmware/", subsystem, "/", name, ret);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return 0;
+
+        return -ENODEV;
+}
+
+_public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
+        assert_return(ret, -EINVAL);
+        assert_return(st, -EINVAL);
+
+        return device_new_from_mode_and_devnum(ret, st->st_mode, st->st_rdev);
+}
+
+_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
+        struct stat st;
+        dev_t devnum;
+        mode_t mode;
+
+        assert_return(ret, -EINVAL);
+        assert_return(devname, -EINVAL);
+
+        /* This function actually accepts both devlinks and devnames, i.e. both symlinks and device
+         * nodes below /dev/. */
+
+        /* Also ignore when the specified path is "/dev". */
+        if (isempty(path_startswith(devname, "/dev")))
+                return -EINVAL;
+
+        if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0)
+                /* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified.
+                 * In that case, we can directly convert the path to syspath, hence it is not necessary
+                 * that the specified path exists. So, this works fine without udevd being running. */
+                return device_new_from_mode_and_devnum(ret, mode, devnum);
+
+        if (stat(devname, &st) < 0)
+                return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno;
+
+        return sd_device_new_from_stat_rdev(ret, &st);
+}
+
+_public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
+        assert_return(ret, -EINVAL);
+        assert_return(path, -EINVAL);
+
+        if (path_startswith(path, "/dev"))
+                return sd_device_new_from_devname(ret, path);
+
+        return device_new_from_syspath(ret, path, /* strict = */ false);
+}
+#endif /* NM_IGNORED */
+
+int device_set_devtype(sd_device *device, const char *devtype) {
+        _cleanup_free_ char *t = NULL;
+        int r;
+
+        assert(device);
+        assert(devtype);
+
+        t = strdup(devtype);
+        if (!t)
+                return -ENOMEM;
+
+        r = device_add_property_internal(device, "DEVTYPE", t);
+        if (r < 0)
+                return r;
+
+        return free_and_replace(device->devtype, t);
+}
+
+int device_set_ifindex(sd_device *device, const char *name) {
+        int r, ifindex;
+
+        assert(device);
+        assert(name);
+
+        ifindex = parse_ifindex(name);
+        if (ifindex < 0)
+                return ifindex;
+
+        r = device_add_property_internal(device, "IFINDEX", name);
+        if (r < 0)
+                return r;
+
+        device->ifindex = ifindex;
+
+        return 0;
+}
+
+static int mangle_devname(const char *p, char **ret) {
+        char *q;
+
+        assert(p);
+        assert(ret);
+
+        if (!path_is_safe(p))
+                return -EINVAL;
+
+        /* When the path is absolute, it must start with "/dev/", but ignore "/dev/" itself. */
+        if (path_is_absolute(p)) {
+                if (isempty(path_startswith(p, "/dev/")))
+                        return -EINVAL;
+
+                q = strdup(p);
+        } else
+                q = path_join("/dev/", p);
+        if (!q)
+                return -ENOMEM;
+
+        path_simplify(q);
+
+        *ret = q;
+        return 0;
+}
+
+int device_set_devname(sd_device *device, const char *devname) {
+        _cleanup_free_ char *t = NULL;
+        int r;
+
+        assert(device);
+        assert(devname);
+
+        r = mangle_devname(devname, &t);
+        if (r < 0)
+                return r;
+
+        r = device_add_property_internal(device, "DEVNAME", t);
+        if (r < 0)
+                return r;
+
+        return free_and_replace(device->devname, t);
+}
+
+int device_set_devmode(sd_device *device, const char *_devmode) {
+        unsigned devmode;
+        int r;
+
+        assert(device);
+        assert(_devmode);
+
+        r = safe_atou(_devmode, &devmode);
+        if (r < 0)
+                return r;
+
+        if (devmode > 07777)
+                return -EINVAL;
+
+        r = device_add_property_internal(device, "DEVMODE", _devmode);
+        if (r < 0)
+                return r;
+
+        device->devmode = devmode;
+
+        return 0;
+}
+
+int device_set_devnum(sd_device *device, const char *major, const char *minor) {
+        unsigned maj, min = 0;
+        int r;
+
+        assert(device);
+        assert(major);
+
+        r = safe_atou(major, &maj);
+        if (r < 0)
+                return r;
+        if (maj == 0)
+                return 0;
+        if (!DEVICE_MAJOR_VALID(maj))
+                return -EINVAL;
+
+        if (minor) {
+                r = safe_atou(minor, &min);
+                if (r < 0)
+                        return r;
+                if (!DEVICE_MINOR_VALID(min))
+                        return -EINVAL;
+        }
+
+        r = device_add_property_internal(device, "MAJOR", major);
+        if (r < 0)
+                return r;
+
+        if (minor) {
+                r = device_add_property_internal(device, "MINOR", minor);
+                if (r < 0)
+                        return r;
+        }
+
+        device->devnum = makedev(maj, min);
+
+        return 0;
+}
+
+int device_set_diskseq(sd_device *device, const char *str) {
+        uint64_t diskseq;
+        int r;
+
+        assert(device);
+        assert(str);
+
+        r = safe_atou64(str, &diskseq);
+        if (r < 0)
+                return r;
+        if (diskseq == 0)
+                return -EINVAL;
+
+        r = device_add_property_internal(device, "DISKSEQ", str);
+        if (r < 0)
+                return r;
+
+        device->diskseq = diskseq;
+
+        return 0;
+}
+
+static int handle_uevent_line(
+                sd_device *device,
+                const char *key,
+                const char *value,
+                const char **major,
+                const char **minor) {
+
+        assert(device);
+        assert(key);
+        assert(value);
+        assert(major);
+        assert(minor);
+
+        if (streq(key, "DEVTYPE"))
+                return device_set_devtype(device, value);
+        if (streq(key, "IFINDEX"))
+                return device_set_ifindex(device, value);
+        if (streq(key, "DEVNAME"))
+                return device_set_devname(device, value);
+        if (streq(key, "DEVMODE"))
+                return device_set_devmode(device, value);
+        if (streq(key, "DISKSEQ"))
+                return device_set_diskseq(device, value);
+        if (streq(key, "MAJOR"))
+                *major = value;
+        else if (streq(key, "MINOR"))
+                *minor = value;
+        else
+                return device_add_property_internal(device, key, value);
+
+        return 0;
+}
+
+int device_read_uevent_file(sd_device *device) {
+        _cleanup_free_ char *uevent = NULL;
+        const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
+        char *path;
+        size_t uevent_len = 0;
+        int r;
+
+        enum {
+                PRE_KEY,
+                KEY,
+                PRE_VALUE,
+                VALUE,
+                INVALID_LINE,
+        } state = PRE_KEY;
+
+        assert(device);
+
+        if (device->uevent_loaded || device->sealed)
+                return 0;
+
+        r = sd_device_get_syspath(device, &syspath);
+        if (r < 0)
+                return r;
+
+        device->uevent_loaded = true;
+
+        path = strjoina(syspath, "/uevent");
+
+        r = read_full_virtual_file(path, &uevent, &uevent_len);
+        if (r == -EACCES || ERRNO_IS_NEG_DEVICE_ABSENT(r))
+                /* The uevent files may be write-only, the device may be already removed, or the device
+                 * may not have the uevent file. */
+                return 0;
+        if (r < 0)
+                return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
+
+        for (size_t i = 0; i < uevent_len; i++)
+                switch (state) {
+                case PRE_KEY:
+                        if (!strchr(NEWLINE, uevent[i])) {
+                                key = &uevent[i];
+
+                                state = KEY;
+                        }
+
+                        break;
+                case KEY:
+                        if (uevent[i] == '=') {
+                                uevent[i] = '\0';
+
+                                state = PRE_VALUE;
+                        } else if (strchr(NEWLINE, uevent[i])) {
+                                uevent[i] = '\0';
+                                log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key);
+
+                                state = PRE_KEY;
+                        }
+
+                        break;
+                case PRE_VALUE:
+                        value = &uevent[i];
+                        state = VALUE;
+
+                        _fallthrough_; /* to handle empty property */
+                case VALUE:
+                        if (strchr(NEWLINE, uevent[i])) {
+                                uevent[i] = '\0';
+
+                                r = handle_uevent_line(device, key, value, &major, &minor);
+                                if (r < 0)
+                                        log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
+
+                                state = PRE_KEY;
+                        }
+
+                        break;
+                default:
+                        assert_not_reached();
+                }
+
+        if (major) {
+                r = device_set_devnum(device, major, minor);
+                if (r < 0)
+                        log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path);
+        }
+
+        return 0;
+}
+
+_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
+        if (device->ifindex <= 0)
+                return -ENOENT;
+
+        if (ifindex)
+                *ifindex = device->ifindex;
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(id, -EINVAL);
+
+        switch (id[0]) {
+        case 'b':
+        case 'c': {
+                dev_t devt;
+
+                if (isempty(id))
+                        return -EINVAL;
+
+                r = parse_devnum(id + 1, &devt);
+                if (r < 0)
+                        return r;
+
+                return sd_device_new_from_devnum(ret, id[0], devt);
+        }
+
+        case 'n': {
+                int ifindex;
+
+                ifindex = parse_ifindex(id + 1);
+                if (ifindex < 0)
+                        return ifindex;
+
+                return sd_device_new_from_ifindex(ret, ifindex);
+        }
+
+        case '+': {
+                const char *subsys, *sep;
+
+                sep = strchr(id + 1, ':');
+                if (!sep || sep - id - 1 > NAME_MAX)
+                        return -EINVAL;
+
+                subsys = memdupa_suffix0(id + 1, sep - id - 1);
+
+                return sd_device_new_from_subsystem_sysname(ret, subsys, sep + 1);
+        }
+
+        default:
+                return -EINVAL;
+        }
+}
+#endif /* NM_IGNORED */
+
+_public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
+        assert_return(device, -EINVAL);
+
+        assert(path_startswith(device->syspath, "/sys/"));
+
+        if (ret)
+                *ret = device->syspath;
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+DEFINE_PRIVATE_HASH_OPS_FULL(
+        device_by_path_hash_ops,
+        char, path_hash_func, path_compare, free,
+        sd_device, sd_device_unref);
+
+static int device_enumerate_children_internal(sd_device *device, const char *subdir, Set **stack, Hashmap **children) {
+        _cleanup_closedir_ DIR *dir = NULL;
+        int r;
+
+        assert(device);
+        assert(stack);
+        assert(children);
+
+        r = device_opendir(device, subdir, &dir);
+        if (r < 0)
+                return r;
+
+        FOREACH_DIRENT_ALL(de, dir, return -errno) {
+                _cleanup_(sd_device_unrefp) sd_device *child = NULL;
+                _cleanup_free_ char *p = NULL;
+
+                if (dot_or_dot_dot(de->d_name))
+                        continue;
+
+                if (!IN_SET(de->d_type, DT_LNK, DT_DIR))
+                        continue;
+
+                if (subdir)
+                        p = path_join(subdir, de->d_name);
+                else
+                        p = strdup(de->d_name);
+                if (!p)
+                        return -ENOMEM;
+
+                /* Try to create child device. */
+                r = sd_device_new_child(&child, device, p);
+                if (r >= 0) {
+                        /* OK, this is a child device, saving it. */
+                        r = hashmap_ensure_put(children, &device_by_path_hash_ops, p, child);
+                        if (r < 0)
+                                return r;
+
+                        TAKE_PTR(p);
+                        TAKE_PTR(child);
+                } else if (r == -ENODEV) {
+                        /* This is not a child device. Push the sub-directory into stack, and read it later. */
+
+                        if (de->d_type == DT_LNK)
+                                /* Do not follow symlinks, otherwise, we will enter an infinite loop, e.g.,
+                                 * /sys/class/block/nvme0n1/subsystem/nvme0n1/subsystem/nvme0n1/subsystem/… */
+                                continue;
+
+                        r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p));
+                        if (r < 0)
+                                return r;
+                } else
+                        return r;
+        }
+
+        return 0;
+}
+
+static int device_enumerate_children(sd_device *device) {
+        _cleanup_hashmap_free_ Hashmap *children = NULL;
+        _cleanup_set_free_ Set *stack = NULL;
+        int r;
+
+        assert(device);
+
+        if (device->children_enumerated)
+                return 0; /* Already enumerated. */
+
+        r = device_enumerate_children_internal(device, NULL, &stack, &children);
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _cleanup_free_ char *subdir = NULL;
+
+                subdir = set_steal_first(stack);
+                if (!subdir)
+                        break;
+
+                r = device_enumerate_children_internal(device, subdir, &stack, &children);
+                if (r < 0)
+                        return r;
+        }
+
+        device->children_enumerated = true;
+        device->children = TAKE_PTR(children);
+        return 1; /* Enumerated. */
+}
+
+_public_ sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix) {
+        int r;
+
+        assert(device);
+
+        r = device_enumerate_children(device);
+        if (r < 0) {
+                log_device_debug_errno(device, r, "sd-device: failed to enumerate child devices: %m");
+                if (ret_suffix)
+                        *ret_suffix = NULL;
+                return NULL;
+        }
+
+        device->children_iterator = ITERATOR_FIRST;
+
+        return sd_device_get_child_next(device, ret_suffix);
+}
+
+_public_ sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix) {
+        sd_device *child;
+
+        assert(device);
+
+        (void) hashmap_iterate(device->children, &device->children_iterator, (void**) &child, (const void**) ret_suffix);
+        return child;
+}
+
+_public_ int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix) {
+        _cleanup_free_ char *path = NULL;
+        sd_device *child;
+        const char *s;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(device, -EINVAL);
+        assert_return(suffix, -EINVAL);
+
+        if (!path_is_safe(suffix))
+                return -EINVAL;
+
+        /* If we have already enumerated children, try to find the child from the cache. */
+        child = hashmap_get(device->children, suffix);
+        if (child) {
+                *ret = sd_device_ref(child);
+                return 0;
+        }
+
+        r = sd_device_get_syspath(device, &s);
+        if (r < 0)
+                return r;
+
+        path = path_join(s, suffix);
+        if (!path)
+                return -ENOMEM;
+
+        return sd_device_new_from_syspath(ret, path);
+}
+
+static int device_new_from_child(sd_device **ret, sd_device *child) {
+        _cleanup_free_ char *path = NULL;
+        const char *syspath;
+        int r;
+
+        assert(ret);
+        assert(child);
+
+        r = sd_device_get_syspath(child, &syspath);
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _cleanup_free_ char *p = NULL;
+
+                r = path_extract_directory(path ?: syspath, &p);
+                if (r < 0)
+                        return r;
+
+                if (path_equal(p, "/sys"))
+                        return -ENODEV;
+
+                r = sd_device_new_from_syspath(ret, p);
+                if (r != -ENODEV)
+                        return r;
+
+                free_and_replace(path, p);
+        }
+}
+
+_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
+        int r;
+
+        assert_return(child, -EINVAL);
+
+        if (!child->parent_set) {
+                r = device_new_from_child(&child->parent, child);
+                if (r < 0 && r != -ENODEV)
+                        return r;
+
+                child->parent_set = true;
+        }
+
+        if (!child->parent)
+                return -ENOENT;
+
+        if (ret)
+                *ret = child->parent;
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+int device_set_subsystem(sd_device *device, const char *subsystem) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert(device);
+
+        if (subsystem) {
+                s = strdup(subsystem);
+                if (!s)
+                        return -ENOMEM;
+        }
+
+        r = device_add_property_internal(device, "SUBSYSTEM", s);
+        if (r < 0)
+                return r;
+
+        device->subsystem_set = true;
+        return free_and_replace(device->subsystem, s);
+}
+
+int device_set_drivers_subsystem(sd_device *device) {
+        _cleanup_free_ char *subsystem = NULL;
+        const char *devpath, *drivers, *p;
+        int r;
+
+        assert(device);
+
+        r = sd_device_get_devpath(device, &devpath);
+        if (r < 0)
+                return r;
+
+        drivers = strstr(devpath, "/drivers/");
+        if (!drivers)
+                drivers = endswith(devpath, "/drivers");
+        if (!drivers)
+                return -EINVAL;
+
+        /* Find the path component immediately before the "/drivers/" string */
+        r = path_find_last_component(devpath, /* accept_dot_dot= */ false, &drivers, &p);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        subsystem = strndup(p, r);
+        if (!subsystem)
+                return -ENOMEM;
+
+        r = device_set_subsystem(device, "drivers");
+        if (r < 0)
+                return r;
+
+        return free_and_replace(device->driver_subsystem, subsystem);
+}
+
+_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        if (!device->subsystem_set) {
+                _cleanup_free_ char *subsystem = NULL;
+                const char *syspath;
+                char *path;
+
+                r = sd_device_get_syspath(device, &syspath);
+                if (r < 0)
+                        return r;
+
+                /* read 'subsystem' link */
+                path = strjoina(syspath, "/subsystem");
+                r = readlink_value(path, &subsystem);
+                if (r < 0 && r != -ENOENT)
+                        return log_device_debug_errno(device, r,
+                                                      "sd-device: Failed to read subsystem for %s: %m",
+                                                      device->devpath);
+
+                if (subsystem)
+                        r = device_set_subsystem(device, subsystem);
+                /* use implicit names */
+                else if (!isempty(path_startswith(device->devpath, "/module/")))
+                        r = device_set_subsystem(device, "module");
+                else if (strstr(syspath, "/drivers/") || endswith(syspath, "/drivers"))
+                        r = device_set_drivers_subsystem(device);
+                else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/")))
+                        r = device_set_subsystem(device, "subsystem");
+                else {
+                        device->subsystem_set = true;
+                        r = 0;
+                }
+                if (r < 0)
+                        return log_device_debug_errno(device, r,
+                                                      "sd-device: Failed to set subsystem for %s: %m",
+                                                      device->devpath);
+        }
+
+        if (!device->subsystem)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->subsystem;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
+        if (!device->devtype)
+                return -ENOENT;
+
+        if (devtype)
+                *devtype = device->devtype;
+
+        return !!device->devtype;
+}
+
+_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *device, const char *subsystem, const char *devtype, sd_device **ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+        assert_return(subsystem, -EINVAL);
+
+        for (;;) {
+                r = sd_device_get_parent(device, &device);
+                if (r < 0)
+                        return r;
+
+                if (!device_in_subsystem(device, subsystem))
+                        continue;
+
+                if (devtype && !device_is_devtype(device, devtype))
+                        continue;
+
+                if (ret)
+                        *ret = device;
+                return 0;
+        }
+}
+#endif /* NM_IGNORED */
+
+_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
+        if (major(device->devnum) <= 0)
+                return -ENOENT;
+
+        if (devnum)
+                *devnum = device->devnum;
+
+        return 0;
+}
+
+int device_set_driver(sd_device *device, const char *driver) {
+        _cleanup_free_ char *d = NULL;
+        int r;
+
+        assert(device);
+
+        if (driver) {
+                d = strdup(driver);
+                if (!d)
+                        return -ENOMEM;
+        }
+
+        r = device_add_property_internal(device, "DRIVER", d);
+        if (r < 0)
+                return r;
+
+        device->driver_set = true;
+        return free_and_replace(device->driver, d);
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
+        assert_return(device, -EINVAL);
+
+        if (!device->driver_set) {
+                _cleanup_free_ char *driver = NULL;
+                const char *syspath;
+                char *path;
+                int r;
+
+                r = sd_device_get_syspath(device, &syspath);
+                if (r < 0)
+                        return r;
+
+                path = strjoina(syspath, "/driver");
+                r = readlink_value(path, &driver);
+                if (r < 0 && r != -ENOENT)
+                        return log_device_debug_errno(device, r,
+                                                      "sd-device: readlink(\"%s\") failed: %m", path);
+
+                r = device_set_driver(device, driver);
+                if (r < 0)
+                        return log_device_debug_errno(device, r,
+                                                      "sd-device: Failed to set driver \"%s\": %m", driver);
+        }
+
+        if (!device->driver)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->driver;
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+_public_ int sd_device_get_devpath(sd_device *device, const char **ret) {
+        assert_return(device, -EINVAL);
+
+        assert(device->devpath);
+        assert(device->devpath[0] == '/');
+
+        if (ret)
+                *ret = device->devpath;
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_device_get_devname(sd_device *device, const char **devname) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
+        if (!device->devname)
+                return -ENOENT;
+
+        assert(!isempty(path_startswith(device->devname, "/dev/")));
+
+        if (devname)
+                *devname = device->devname;
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+static int device_set_sysname_and_sysnum(sd_device *device) {
+        _cleanup_free_ char *sysname = NULL;
+        size_t len, n;
+        int r;
+
+        assert(device);
+
+        r = path_extract_filename(device->devpath, &sysname);
+        if (r < 0)
+                return r;
+        if (r == O_DIRECTORY)
+                return -EINVAL;
+
+        /* some devices have '!' in their name, change that to '/' */
+        string_replace_char(sysname, '!', '/');
+
+        n = strspn_from_end(sysname, DIGITS);
+        len = strlen(sysname);
+        assert(n <= len);
+        if (n == len)
+                n = 0; /* Do not set sysnum for number only sysname. */
+
+        device->sysnum = n > 0 ? sysname + len - n : NULL;
+        return free_and_replace(device->sysname, sysname);
+}
+
+_public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        if (!device->sysname) {
+                r = device_set_sysname_and_sysnum(device);
+                if (r < 0)
+                        return r;
+        }
+
+        if (ret)
+                *ret = device->sysname;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        if (!device->sysname) {
+                r = device_set_sysname_and_sysnum(device);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!device->sysnum)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->sysnum;
+        return 0;
+}
+
+_public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
+        assert_return(device, -EINVAL);
+
+        if (device->action < 0)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->action;
+
+        return 0;
+}
+
+_public_ int sd_device_get_seqnum(sd_device *device, uint64_t *ret) {
+        assert_return(device, -EINVAL);
+
+        if (device->seqnum == 0)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->seqnum;
+
+        return 0;
+}
+
+_public_ int sd_device_get_diskseq(sd_device *device, uint64_t *ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
+        if (device->diskseq == 0)
+                return -ENOENT;
+
+        if (ret)
+                *ret = device->diskseq;
+
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+static bool is_valid_tag(const char *tag) {
+        assert(tag);
+
+        return in_charset(tag, ALPHANUMERICAL "-_") && filename_is_valid(tag);
+}
+
+int device_add_tag(sd_device *device, const char *tag, bool both) {
+        int r, added;
+
+        assert(device);
+        assert(tag);
+
+        if (!is_valid_tag(tag))
+                return -EINVAL;
+
+        /* Definitely add to the "all" list of tags (i.e. the sticky list) */
+        added = set_put_strdup(&device->all_tags, tag);
+        if (added < 0)
+                return added;
+
+        /* And optionally, also add it to the current list of tags */
+        if (both) {
+                r = set_put_strdup(&device->current_tags, tag);
+                if (r < 0) {
+                        if (added > 0)
+                                (void) set_remove(device->all_tags, tag);
+
+                        return r;
+                }
+        }
+
+        device->tags_generation++;
+        device->property_tags_outdated = true;
+
+        return 0;
+}
+
+int device_add_devlink(sd_device *device, const char *devlink) {
+        char *p;
+        int r;
+
+        assert(device);
+        assert(devlink);
+
+        r = mangle_devname(devlink, &p);
+        if (r < 0)
+                return r;
+
+        r = set_ensure_consume(&device->devlinks, &path_hash_ops_free, p);
+        if (r < 0)
+                return r;
+
+        device->devlinks_generation++;
+        device->property_devlinks_outdated = true;
+
+        return r; /* return 1 when newly added, 0 when already exists */
+}
+
+#if 0 /* NM_IGNORED */
+int device_remove_devlink(sd_device *device, const char *devlink) {
+        _cleanup_free_ char *p = NULL, *s = NULL;
+        int r;
+
+        assert(device);
+        assert(devlink);
+
+        r = mangle_devname(devlink, &p);
+        if (r < 0)
+                return r;
+
+        s = set_remove(device->devlinks, p);
+        if (!s)
+                return 0; /* does not exist */
+
+        device->devlinks_generation++;
+        device->property_devlinks_outdated = true;
+        return 1; /* removed */
+}
+
+bool device_has_devlink(sd_device *device, const char *devlink) {
+        assert(device);
+        assert(devlink);
+
+        return set_contains(device->devlinks, devlink);
+}
+#endif /* NM_IGNORED */
+
+static int device_add_property_internal_from_string(sd_device *device, const char *str) {
+        _cleanup_free_ char *key = NULL;
+        char *value;
+        int r;
+
+        assert(device);
+        assert(str);
+
+        key = strdup(str);
+        if (!key)
+                return -ENOMEM;
+
+        value = strchr(key, '=');
+        if (!value)
+                return -EINVAL;
+
+        *value = '\0';
+
+        if (isempty(++value))
+                value = NULL;
+
+        /* Add the property to both sd_device::properties and sd_device::properties_db,
+         * as this is called by only handle_db_line(). */
+        r = device_add_property_aux(device, key, value, false);
+        if (r < 0)
+                return r;
+
+        return device_add_property_aux(device, key, value, true);
+}
+
+int device_set_usec_initialized(sd_device *device, usec_t when) {
+        char s[DECIMAL_STR_MAX(usec_t)];
+        int r;
+
+        assert(device);
+
+        xsprintf(s, USEC_FMT, when);
+
+        r = device_add_property_internal(device, "USEC_INITIALIZED", s);
+        if (r < 0)
+                return r;
+
+        device->usec_initialized = when;
+        return 0;
+}
+
+static int handle_db_line(sd_device *device, char key, const char *value) {
+        int r;
+
+        assert(device);
+        assert(value);
+
+        switch (key) {
+        case 'G': /* Any tag */
+        case 'Q': /* Current tag */
+                return device_add_tag(device, value, key == 'Q');
+
+        case 'S': {
+                const char *path;
+
+                path = strjoina("/dev/", value);
+                return device_add_devlink(device, path);
+        }
+        case 'E':
+                return device_add_property_internal_from_string(device, value);
+
+        case 'I': {
+                usec_t t;
+
+                r = safe_atou64(value, &t);
+                if (r < 0)
+                        return r;
+
+                return device_set_usec_initialized(device, t);
+        }
+        case 'L':
+                return safe_atoi(value, &device->devlink_priority);
+
+        case 'W':
+                /* Deprecated. Previously, watch handle is both saved in database and /run/udev/watch.
+                 * However, the handle saved in database may not be updated when the handle is updated
+                 * or removed. Moreover, it is not necessary to store the handle within the database,
+                 * as its value becomes meaningless when udevd is restarted. */
+                return 0;
+
+        case 'V':
+                return safe_atou(value, &device->database_version);
+
+        default:
+                log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
+                return 0;
+        }
+}
+
+int device_get_device_id(sd_device *device, const char **ret) {
+        assert(device);
+        assert(ret);
+
+        if (!device->device_id) {
+                _cleanup_free_ char *id = NULL;
+                const char *subsystem;
+                dev_t devnum;
+                int ifindex, r;
+
+                r = sd_device_get_subsystem(device, &subsystem);
+                if (r < 0)
+                        return r;
+
+                if (sd_device_get_devnum(device, &devnum) >= 0) {
+                        /* use dev_t — b259:131072, c254:0 */
+                        if (asprintf(&id, "%c" DEVNUM_FORMAT_STR,
+                                     streq(subsystem, "block") ? 'b' : 'c',
+                                     DEVNUM_FORMAT_VAL(devnum)) < 0)
+                                return -ENOMEM;
+                } else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
+                        /* use netdev ifindex — n3 */
+                        if (asprintf(&id, "n%u", (unsigned) ifindex) < 0)
+                                return -ENOMEM;
+                } else {
+                        _cleanup_free_ char *sysname = NULL;
+
+                        /* use $subsys:$sysname — pci:0000:00:1f.2
+                         * sd_device_get_sysname() has '!' translated, get it from devpath */
+                        r = path_extract_filename(device->devpath, &sysname);
+                        if (r < 0)
+                                return r;
+                        if (r == O_DIRECTORY)
+                                return -EINVAL;
+
+                        if (streq(subsystem, "drivers")) {
+                                /* the 'drivers' pseudo-subsystem is special, and needs the real
+                                 * subsystem encoded as well */
+                                assert(device->driver_subsystem);
+                                id = strjoin("+drivers:", device->driver_subsystem, ":", sysname);
+                        } else
+                                id = strjoin("+", subsystem, ":", sysname);
+                        if (!id)
+                                return -ENOMEM;
+                }
+
+                if (!filename_is_valid(id))
+                        return -EINVAL;
+
+                device->device_id = TAKE_PTR(id);
+        }
+
+        *ret = device->device_id;
+        return 0;
+}
+
+int device_read_db_internal_filename(sd_device *device, const char *filename) {
+        _cleanup_free_ char *db = NULL;
+        const char *value = NULL;
+        size_t db_len = 0;
+        char key = '\0';  /* Unnecessary initialization to appease gcc-12.0.0-0.4.fc36 */
+        int r;
+
+        enum {
+                PRE_KEY,
+                KEY,
+                PRE_VALUE,
+                VALUE,
+                INVALID_LINE,
+        } state = PRE_KEY;
+
+        assert(device);
+        assert(filename);
+
+        r = read_full_file(filename, &db, &db_len);
+        if (r < 0) {
+                if (r == -ENOENT)
+                        return 0;
+
+                return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", filename);
+        }
+
+        /* devices with a database entry are initialized */
+        device->is_initialized = true;
+
+        device->db_loaded = true;
+
+        for (size_t i = 0; i < db_len; i++)
+                switch (state) {
+                case PRE_KEY:
+                        if (!strchr(NEWLINE, db[i])) {
+                                key = db[i];
+
+                                state = KEY;
+                        }
+
+                        break;
+                case KEY:
+                        if (db[i] != ':') {
+                                log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key);
+
+                                state = INVALID_LINE;
+                        } else {
+                                db[i] = '\0';
+
+                                state = PRE_VALUE;
+                        }
+
+                        break;
+                case PRE_VALUE:
+                        value = &db[i];
+
+                        state = VALUE;
+
+                        break;
+                case INVALID_LINE:
+                        if (strchr(NEWLINE, db[i]))
+                                state = PRE_KEY;
+
+                        break;
+                case VALUE:
+                        if (strchr(NEWLINE, db[i])) {
+                                db[i] = '\0';
+                                r = handle_db_line(device, key, value);
+                                if (r < 0)
+                                        log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m",
+                                                               key, value);
+
+                                state = PRE_KEY;
+                        }
+
+                        break;
+                default:
+                        return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax.");
+                }
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_device_get_is_initialized(sd_device *device) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = device_read_db(device);
+        if (r == -ENOENT)
+                /* The device may be already removed or renamed. */
+                return false;
+        if (r < 0)
+                return r;
+
+        return device->is_initialized;
+}
+
+_public_ int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = sd_device_get_is_initialized(device);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EBUSY;
+
+        if (device->usec_initialized == 0)
+                return -ENODATA;
+
+        if (ret)
+                *ret = device->usec_initialized;
+
+        return 0;
+}
+
+_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret) {
+        usec_t now_ts, ts;
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        r = sd_device_get_usec_initialized(device, &ts);
+        if (r < 0)
+                return r;
+
+        now_ts = now(CLOCK_MONOTONIC);
+
+        if (now_ts < ts)
+                return -EIO;
+
+        if (ret)
+                *ret = usec_sub_unsigned(now_ts, ts);
+
+        return 0;
+}
+
+_public_ const char *sd_device_get_tag_first(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        (void) device_read_db(device);
+
+        device->all_tags_iterator_generation = device->tags_generation;
+        device->all_tags_iterator = ITERATOR_FIRST;
+
+        (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v);
+        return v;
+}
+
+_public_ const char *sd_device_get_tag_next(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        (void) device_read_db(device);
+
+        if (device->all_tags_iterator_generation != device->tags_generation)
+                return NULL;
+
+        (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v);
+        return v;
+}
+
+static bool device_database_supports_current_tags(sd_device *device) {
+        assert(device);
+
+        (void) device_read_db(device);
+
+        /* The current tags (saved in Q field) feature is implemented in database version 1.
+         * If the database version is 0, then the tags (NOT current tags, saved in G field) are not
+         * sticky. Thus, we can safely bypass the operations for the current tags (Q) to tags (G). */
+
+        return device->database_version >= 1;
+}
+
+_public_ const char *sd_device_get_current_tag_first(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        if (!device_database_supports_current_tags(device))
+                return sd_device_get_tag_first(device);
+
+        (void) device_read_db(device);
+
+        device->current_tags_iterator_generation = device->tags_generation;
+        device->current_tags_iterator = ITERATOR_FIRST;
+
+        (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v);
+        return v;
+}
+
+_public_ const char *sd_device_get_current_tag_next(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        if (!device_database_supports_current_tags(device))
+                return sd_device_get_tag_next(device);
+
+        (void) device_read_db(device);
+
+        if (device->current_tags_iterator_generation != device->tags_generation)
+                return NULL;
+
+        (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v);
+        return v;
+}
+
+_public_ const char *sd_device_get_devlink_first(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        (void) device_read_db(device);
+
+        device->devlinks_iterator_generation = device->devlinks_generation;
+        device->devlinks_iterator = ITERATOR_FIRST;
+
+        (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
+        return v;
+}
+
+_public_ const char *sd_device_get_devlink_next(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        (void) device_read_db(device);
+
+        if (device->devlinks_iterator_generation != device->devlinks_generation)
+                return NULL;
+
+        (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v);
+        return v;
+}
+#endif /* NM_IGNORED */
+
+int device_properties_prepare(sd_device *device) {
+        int r;
+
+        assert(device);
+
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
+        r = device_read_db(device);
+        if (r < 0)
+                return r;
+
+        if (device->property_devlinks_outdated) {
+                _cleanup_free_ char *devlinks = NULL;
+
+                r = set_strjoin(device->devlinks, " ", false, &devlinks);
+                if (r < 0)
+                        return r;
+
+                if (!isempty(devlinks)) {
+                        r = device_add_property_internal(device, "DEVLINKS", devlinks);
+                        if (r < 0)
+                                return r;
+                }
+
+                device->property_devlinks_outdated = false;
+        }
+
+        if (device->property_tags_outdated) {
+                _cleanup_free_ char *tags = NULL;
+
+                r = set_strjoin(device->all_tags, ":", true, &tags);
+                if (r < 0)
+                        return r;
+
+                if (!isempty(tags)) {
+                        r = device_add_property_internal(device, "TAGS", tags);
+                        if (r < 0)
+                                return r;
+                }
+
+                tags = mfree(tags);
+                r = set_strjoin(device->current_tags, ":", true, &tags);
+                if (r < 0)
+                        return r;
+
+                if (!isempty(tags)) {
+                        r = device_add_property_internal(device, "CURRENT_TAGS", tags);
+                        if (r < 0)
+                                return r;
+                }
+
+                device->property_tags_outdated = false;
+        }
+
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) {
+        const char *key;
+        int r;
+
+        assert_return(device, NULL);
+
+        r = device_properties_prepare(device);
+        if (r < 0)
+                return NULL;
+
+        device->properties_iterator_generation = device->properties_generation;
+        device->properties_iterator = ITERATOR_FIRST;
+
+        (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key);
+        return key;
+}
+
+_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) {
+        const char *key;
+        int r;
+
+        assert_return(device, NULL);
+
+        r = device_properties_prepare(device);
+        if (r < 0)
+                return NULL;
+
+        if (device->properties_iterator_generation != device->properties_generation)
+                return NULL;
+
+        (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key);
+        return key;
+}
+
+static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir, Set **stack) {
+        _cleanup_closedir_ DIR *dir = NULL;
+        int r;
+
+        assert(device);
+        assert(stack);
+
+        r = device_opendir(device, subdir, &dir);
+        if (r == -ENOENT && subdir)
+                return 0; /* Maybe, this is a child device, and is already removed. */
+        if (r < 0)
+                return r;
+
+        if (subdir) {
+                if (faccessat(dirfd(dir), "uevent", F_OK, 0) >= 0)
+                        return 0; /* this is a child device, skipping */
+                if (errno != ENOENT) {
+                        log_device_debug_errno(device, errno,
+                                               "sd-device: Failed to access %s/uevent, ignoring sub-directory %s: %m",
+                                               subdir, subdir);
+                        return 0;
+                }
+        }
+
+        FOREACH_DIRENT_ALL(de, dir, return -errno) {
+                _cleanup_free_ char *p = NULL;
+                struct stat statbuf;
+
+                if (dot_or_dot_dot(de->d_name))
+                        continue;
+
+                /* only handle symlinks, regular files, and directories */
+                if (!IN_SET(de->d_type, DT_LNK, DT_REG, DT_DIR))
+                        continue;
+
+                if (subdir) {
+                        p = path_join(subdir, de->d_name);
+                        if (!p)
+                                return -ENOMEM;
+                }
+
+                if (de->d_type == DT_DIR) {
+                        /* push the sub-directory into the stack, and read it later. */
+                        if (p)
+                                r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p));
+                        else
+                                r = set_put_strdup_full(stack, &path_hash_ops_free, de->d_name);
+                        if (r < 0)
+                                return r;
+
+                        continue;
+                }
+
+                if (fstatat(dirfd(dir), de->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) < 0)
+                        continue;
+
+                if ((statbuf.st_mode & (S_IRUSR | S_IWUSR)) == 0)
+                        continue;
+
+                if (p)
+                        r = set_ensure_consume(&device->sysattrs, &path_hash_ops_free, TAKE_PTR(p));
+                else
+                        r = set_put_strdup_full(&device->sysattrs, &path_hash_ops_free, de->d_name);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int device_sysattrs_read_all(sd_device *device) {
+        _cleanup_set_free_ Set *stack = NULL;
+        int r;
+
+        assert(device);
+
+        if (device->sysattrs_read)
+                return 0;
+
+        r = device_sysattrs_read_all_internal(device, NULL, &stack);
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _cleanup_free_ char *subdir = NULL;
+
+                subdir = set_steal_first(stack);
+                if (!subdir)
+                        break;
+
+                r = device_sysattrs_read_all_internal(device, subdir, &stack);
+                if (r < 0)
+                        return r;
+        }
+
+        device->sysattrs_read = true;
+
+        return 0;
+}
+
+_public_ const char *sd_device_get_sysattr_first(sd_device *device) {
+        void *v;
+        int r;
+
+        assert_return(device, NULL);
+
+        if (!device->sysattrs_read) {
+                r = device_sysattrs_read_all(device);
+                if (r < 0) {
+                        errno = -r;
+                        return NULL;
+                }
+        }
+
+        device->sysattrs_iterator = ITERATOR_FIRST;
+
+        (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
+        return v;
+}
+
+_public_ const char *sd_device_get_sysattr_next(sd_device *device) {
+        void *v;
+
+        assert_return(device, NULL);
+
+        if (!device->sysattrs_read)
+                return NULL;
+
+        (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v);
+        return v;
+}
+
+_public_ int sd_device_has_tag(sd_device *device, const char *tag) {
+        assert_return(device, -EINVAL);
+        assert_return(tag, -EINVAL);
+
+        (void) device_read_db(device);
+
+        return set_contains(device->all_tags, tag);
+}
+
+_public_ int sd_device_has_current_tag(sd_device *device, const char *tag) {
+        assert_return(device, -EINVAL);
+        assert_return(tag, -EINVAL);
+
+        if (!device_database_supports_current_tags(device))
+                return sd_device_has_tag(device, tag);
+
+        (void) device_read_db(device);
+
+        return set_contains(device->current_tags, tag);
+}
+#endif /* NM_IGNORED */
+
+_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **ret_value) {
+        const char *value;
+        int r;
+
+        assert_return(device, -EINVAL);
+        assert_return(key, -EINVAL);
+
+        r = device_properties_prepare(device);
+        if (r < 0)
+                return r;
+
+        value = ordered_hashmap_get(device->properties, key);
+        if (!value)
+                return -ENOENT;
+
+        if (ret_value)
+                *ret_value = value;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int device_get_property_bool(sd_device *device, const char *key) {
+        const char *value;
+        int r;
+
+        assert(device);
+        assert(key);
+
+        r = sd_device_get_property_value(device, key, &value);
+        if (r < 0)
+                return r;
+
+        return parse_boolean(value);
+}
+
+int device_get_property_int(sd_device *device, const char *key, int *ret) {
+        const char *value;
+        int r, v;
+
+        assert(device);
+        assert(key);
+
+        r = sd_device_get_property_value(device, key, &value);
+        if (r < 0)
+                return r;
+
+        r = safe_atoi(value, &v);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = v;
+        return 0;
+}
+
+_public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
+        const char *s;
+        sd_id128_t id;
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        /* Retrieves the UUID attached to a uevent when triggering it from userspace via
+         * sd_device_trigger_with_uuid() or an equivalent interface. Returns -ENOENT if the record is not
+         * caused by a synthetic event and -ENODATA if it was but no UUID was specified */
+
+        r = sd_device_get_property_value(device, "SYNTH_UUID", &s);
+        if (r < 0)
+                return r;
+
+        if (streq(s, "0")) /* SYNTH_UUID=0 is set whenever a device is triggered by userspace without specifying a UUID */
+                return -ENODATA;
+
+        r = sd_id128_from_string(s, &id);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = id;
+
+        return 0;
+}
+
+void device_clear_sysattr_cache(sd_device *device) {
+        device->sysattr_values = hashmap_free(device->sysattr_values);
+}
+
+int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
+        _unused_ _cleanup_free_ char *old_value = NULL;
+        _cleanup_free_ char *new_key = NULL;
+        int r;
+
+        assert(device);
+        assert(key);
+
+        /* This takes the reference of the input value. The input value may be NULL.
+         * This replaces the value if it already exists. */
+
+        /* First, remove the old cache entry. So, we do not need to clear cache on error. */
+        old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key);
+        if (!new_key) {
+                new_key = strdup(key);
+                if (!new_key)
+                        return -ENOMEM;
+        }
+
+        r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(new_key);
+
+        return 0;
+}
+
+int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
+        const char *k = NULL, *value;
+
+        assert(device);
+        assert(key);
+
+        value = hashmap_get2(device->sysattr_values, key, (void **) &k);
+        if (!k)
+                return -ESTALE; /* We have not read the attribute. */
+        if (!value)
+                return -ENOENT; /* We have looked up the attribute before and it did not exist. */
+        if (ret_value)
+                *ret_value = value;
+        return 0;
+}
+
+/* We cache all sysattr lookups. If an attribute does not exist, it is stored
+ * with a NULL value in the cache, otherwise the returned string is stored */
+_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
+        _cleanup_free_ char *value = NULL, *path = NULL;
+        const char *syspath;
+        struct stat statbuf;
+        int r;
+
+        assert_return(device, -EINVAL);
+        assert_return(sysattr, -EINVAL);
+
+        /* look for possibly already cached result */
+        r = device_get_cached_sysattr_value(device, sysattr, ret_value);
+        if (r != -ESTALE)
+                return r;
+
+        r = sd_device_get_syspath(device, &syspath);
+        if (r < 0)
+                return r;
+
+        path = path_join(syspath, sysattr);
+        if (!path)
+                return -ENOMEM;
+
+        if (lstat(path, &statbuf) < 0) {
+                int k;
+
+                r = -errno;
+
+                /* remember that we could not access the sysattr */
+                k = device_cache_sysattr_value(device, sysattr, NULL);
+                if (k < 0)
+                        log_device_debug_errno(device, k,
+                                               "sd-device: failed to cache attribute '%s' with NULL, ignoring: %m",
+                                               sysattr);
+
+                return r;
+        } else if (S_ISLNK(statbuf.st_mode)) {
+                /* Some core links return only the last element of the target path,
+                 * these are just values, the paths should not be exposed. */
+                if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
+                        r = readlink_value(path, &value);
+                        if (r < 0)
+                                return r;
+                } else
+                        return -EINVAL;
+        } else if (S_ISDIR(statbuf.st_mode))
+                /* skip directories */
+                return -EISDIR;
+        else if (!(statbuf.st_mode & S_IRUSR))
+                /* skip non-readable files */
+                return -EPERM;
+        else {
+                size_t size;
+
+                /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to
+                 * also get the size of the result. See issue #20025. */
+                r = read_full_virtual_file(path, &value, &size);
+                if (r < 0)
+                        return r;
+
+                /* drop trailing newlines */
+                while (size > 0 && strchr(NEWLINE, value[--size]))
+                        value[size] = '\0';
+        }
+
+        /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
+         * sysattr value is critical unlike the other places. */
+        r = device_cache_sysattr_value(device, sysattr, value);
+        if (r < 0) {
+                log_device_debug_errno(device, r,
+                                       "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
+                                       sysattr, value, ret_value ? "" : ", ignoring");
+                if (ret_value)
+                        return r;
+
+                return 0;
+        }
+
+        if (ret_value)
+                *ret_value = value;
+
+        TAKE_PTR(value);
+        return 0;
+}
+
+int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
+        const char *value;
+        int r;
+
+        r = sd_device_get_sysattr_value(device, sysattr, &value);
+        if (r < 0)
+                return r;
+
+        int v;
+        r = safe_atoi(value, &v);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
+
+        if (ret_value)
+                *ret_value = v;
+        /* We return "true" if the value is positive. */
+        return v > 0;
+}
+
+int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+        const char *value;
+        int r;
+
+        r = sd_device_get_sysattr_value(device, sysattr, &value);
+        if (r < 0)
+                return r;
+
+        unsigned v;
+        r = safe_atou(value, &v);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
+
+        if (ret_value)
+                *ret_value = v;
+        /* We return "true" if the value is positive. */
+        return v > 0;
+}
+
+int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value) {
+        const char *value;
+        int r;
+
+        r = sd_device_get_sysattr_value(device, sysattr, &value);
+        if (r < 0)
+                return r;
+
+        uint32_t v;
+        r = safe_atou32(value, &v);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
+
+        if (ret_value)
+                *ret_value = v;
+        /* We return "true" if the value is positive. */
+        return v > 0;
+}
+
+int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
+        const char *value;
+        int r;
+
+        assert(device);
+        assert(sysattr);
+
+        r = sd_device_get_sysattr_value(device, sysattr, &value);
+        if (r < 0)
+                return r;
+
+        return parse_boolean(value);
+}
+
+static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
+        _cleanup_free_ char *key = NULL;
+
+        assert(device);
+        assert(_key);
+
+        free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
+}
+
+_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
+        _cleanup_free_ char *value = NULL, *path = NULL;
+        const char *syspath;
+        size_t len;
+        int r;
+
+        assert_return(device, -EINVAL);
+        assert_return(sysattr, -EINVAL);
+
+        /* Set the attribute and save it in the cache. */
+
+        if (!_value) {
+                /* If input value is NULL, then clear cache and not write anything. */
+                device_remove_cached_sysattr_value(device, sysattr);
+                return 0;
+        }
+
+        r = sd_device_get_syspath(device, &syspath);
+        if (r < 0)
+                return r;
+
+        path = path_join(syspath, sysattr);
+        if (!path)
+                return -ENOMEM;
+
+        len = strlen(_value);
+
+        /* drop trailing newlines */
+        while (len > 0 && strchr(NEWLINE, _value[len - 1]))
+                len--;
+
+        /* value length is limited to 4k */
+        if (len > 4096)
+                return -EINVAL;
+
+        value = strndup(_value, len);
+        if (!value)
+                return -ENOMEM;
+
+        r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
+        if (r < 0) {
+                /* On failure, clear cache entry, as we do not know how it fails. */
+                device_remove_cached_sysattr_value(device, sysattr);
+                return r;
+        }
+
+        /* Do not cache action string written into uevent file. */
+        if (streq(sysattr, "uevent"))
+                return 0;
+
+        r = device_cache_sysattr_value(device, sysattr, value);
+        if (r < 0)
+                log_device_debug_errno(device, r,
+                                       "sd-device: failed to cache attribute '%s' with '%s', ignoring: %m",
+                                       sysattr, value);
+        else
+                TAKE_PTR(value);
+
+        return 0;
+}
+
+_public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) {
+        _cleanup_free_ char *value = NULL;
+        va_list ap;
+        int r;
+
+        assert_return(device, -EINVAL);
+        assert_return(sysattr, -EINVAL);
+
+        if (!format) {
+                device_remove_cached_sysattr_value(device, sysattr);
+                return 0;
+        }
+
+        va_start(ap, format);
+        r = vasprintf(&value, format, ap);
+        va_end(ap);
+
+        if (r < 0)
+                return -ENOMEM;
+
+        return sd_device_set_sysattr_value(device, sysattr, value);
+}
+
+_public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
+        const char *s;
+
+        assert_return(device, -EINVAL);
+
+        s = device_action_to_string(action);
+        if (!s)
+                return -EINVAL;
+
+        /* This uses the simple no-UUID interface of kernel < 4.13 */
+        return sd_device_set_sysattr_value(device, "uevent", s);
+}
+
+_public_ int sd_device_trigger_with_uuid(
+                sd_device *device,
+                sd_device_action_t action,
+                sd_id128_t *ret_uuid) {
+
+        const char *s, *j;
+        sd_id128_t u;
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        /* If no one wants to know the UUID, use the simple interface from pre-4.13 times */
+        if (!ret_uuid)
+                return sd_device_trigger(device, action);
+
+        s = device_action_to_string(action);
+        if (!s)
+                return -EINVAL;
+
+        r = sd_id128_randomize(&u);
+        if (r < 0)
+                return r;
+
+        j = strjoina(s, " ", SD_ID128_TO_UUID_STRING(u));
+
+        r = sd_device_set_sysattr_value(device, "uevent", j);
+        if (r < 0)
+                return r;
+
+        *ret_uuid = u;
+        return 0;
+}
+
+_public_ int sd_device_open(sd_device *device, int flags) {
+        _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
+        const char *devname;
+        uint64_t q, diskseq = 0;
+        struct stat st;
+        dev_t devnum;
+        int r;
+
+        assert_return(device, -EINVAL);
+        assert_return(FLAGS_SET(flags, O_PATH) || !FLAGS_SET(flags, O_NOFOLLOW), -EINVAL);
+
+        r = sd_device_get_devname(device, &devname);
+        if (r == -ENOENT)
+                return -ENOEXEC;
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_devnum(device, &devnum);
+        if (r == -ENOENT)
+                return -ENOEXEC;
+        if (r < 0)
+                return r;
+
+        fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
+        if (fd < 0)
+                return -errno;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (st.st_rdev != devnum)
+                return -ENXIO;
+
+        if (device_in_subsystem(device, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
+                return -ENXIO;
+
+        /* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */
+        if (FLAGS_SET(flags, O_PATH))
+                return TAKE_FD(fd);
+
+        /* If the device is not initialized, then we cannot determine if we should check diskseq through
+         * ID_IGNORE_DISKSEQ property. Let's skip to check diskseq in that case. */
+        r = sd_device_get_is_initialized(device);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                r = device_get_property_bool(device, "ID_IGNORE_DISKSEQ");
+                if (r < 0 && r != -ENOENT)
+                        return r;
+                if (r <= 0) {
+                        r = sd_device_get_diskseq(device, &diskseq);
+                        if (r < 0 && r != -ENOENT)
+                                return r;
+                }
+        }
+
+        fd2 = fd_reopen(fd, flags);
+        if (fd2 < 0)
+                return fd2;
+
+        if (diskseq == 0)
+                return TAKE_FD(fd2);
+
+        r = fd_get_diskseq(fd2, &q);
+        if (r < 0)
+                return r;
+
+        if (q != diskseq)
+                return -ENXIO;
+
+        return TAKE_FD(fd2);
+}
+
+int device_opendir(sd_device *device, const char *subdir, DIR **ret) {
+        _cleanup_closedir_ DIR *d = NULL;
+        _cleanup_free_ char *path = NULL;
+        const char *syspath;
+        int r;
+
+        assert(device);
+        assert(ret);
+
+        r = sd_device_get_syspath(device, &syspath);
+        if (r < 0)
+                return r;
+
+        if (subdir) {
+                if (!path_is_safe(subdir))
+                        return -EINVAL;
+
+                path = path_join(syspath, subdir);
+                if (!path)
+                        return -ENOMEM;
+        }
+
+        d = opendir(path ?: syspath);
+        if (!d)
+                return -errno;
+
+        *ret = TAKE_PTR(d);
+        return 0;
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
index 663296f4..ef0c2d2a 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
@@ -99,16 +99,21 @@ int event_reset_time_relative(
                 const char *description,
                 bool force_reset) {
 
-        usec_t usec_now;
         int r;
 
         assert(e);
 
-        r = sd_event_now(e, clock, &usec_now);
-        if (r < 0)
-                return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
+        if (usec > 0) {
+                usec_t usec_now;
+
+                r = sd_event_now(e, clock, &usec_now);
+                if (r < 0)
+                        return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
 
-        return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
+                usec = usec_add(usec_now, usec);
+        }
+
+        return event_reset_time(e, s, clock, usec, accuracy, callback, userdata, priority, description, force_reset);
 }
 
 #if 0 /* NM_IGNORED */
@@ -149,4 +154,21 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
 
         return 0;
 }
+
+int event_add_child_pidref(
+                sd_event *e,
+                sd_event_source **s,
+                const PidRef *pid,
+                int options,
+                sd_event_child_handler_t callback,
+                void *userdata) {
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        if (pid->fd >= 0)
+                return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
+
+        return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
index c1855844..ad0f2e78 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
@@ -5,6 +5,8 @@
 
 #include "sd-event.h"
 
+#include "pidref.h"
+
 int event_reset_time(
                 sd_event *e,
                 sd_event_source **s,
@@ -32,3 +34,7 @@ static inline int event_source_disable(sd_event_source *s) {
 }
 
 int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
+
+#if 0 /* NM_IGNORED */
+int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
index 5c7ba182..6449b85c 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
@@ -1169,10 +1169,10 @@ static int source_set_pending(sd_event_source *s, bool b) {
                 assert(s->inotify.inode_data->inotify_data);
 
                 if (b)
-                        s->inotify.inode_data->inotify_data->n_pending ++;
+                        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 --;
+                        s->inotify.inode_data->inotify_data->n_pending--;
                 }
         }
 
@@ -1579,7 +1579,7 @@ static int child_exit_callback(sd_event_source *s, const siginfo_t *si, void *us
 
 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;
+        return secure_getenv_bool("SYSTEMD_PIDFD") != 0;
 }
 
 _public_ int sd_event_add_child(
@@ -1983,7 +1983,7 @@ _public_ int sd_event_add_memory_pressure(
 
                 env = secure_getenv("MEMORY_PRESSURE_WRITE");
                 if (env) {
-                        r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size);
+                        r = unbase64mem(env, &write_buffer, &write_buffer_size);
                         if (r < 0)
                                 return r;
                 }
@@ -2239,8 +2239,8 @@ static int inode_data_compare(const struct inode_data *x, const struct inode_dat
 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);
+        siphash24_compress_typesafe(d->dev, state);
+        siphash24_compress_typesafe(d->ino, state);
 }
 
 DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
@@ -4008,7 +4008,7 @@ static int process_inotify(sd_event *e) {
                 if (r < 0)
                         return r;
                 if (r > 0)
-                        done ++;
+                        done++;
         }
 
         return done;
@@ -4620,7 +4620,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
 
         /* Set timestamp only when this is called first time. */
         if (threshold == INT64_MAX)
-                triple_timestamp_get(&e->timestamp);
+                triple_timestamp_now(&e->timestamp);
 
         for (size_t i = 0; i < m; i++) {
 
@@ -4923,13 +4923,13 @@ _public_ int sd_event_get_state(sd_event *e) {
 _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
         assert_return(e, -EINVAL);
         assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(code, -EINVAL);
         assert_return(!event_origin_changed(e), -ECHILD);
 
         if (!e->exit_requested)
                 return -ENODATA;
 
-        *code = e->exit_code;
+        if (code)
+                *code = e->exit_code;
         return 0;
 }
 
@@ -5047,7 +5047,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
                 }
         }
 
-        e->watchdog = !!b;
+        e->watchdog = b;
         return e->watchdog;
 
 fail:
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
index c20f7325..58173055 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
@@ -11,9 +11,29 @@
 #include "hexdecoct.h"
 #include "id128-util.h"
 #include "io-util.h"
+#include "sha256.h"
 #include "stdio-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "sync-util.h"
+#include "virt.h"
+
+int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
+        sd_id128_t t;
+        int r;
+
+        assert(ret);
+
+        r = sd_id128_from_string(ASSERT_PTR(s), &t);
+        if (r < 0)
+                return r;
+
+        if (sd_id128_is_null(t))
+                return -ENXIO;
+
+        *ret = t;
+        return 0;
+}
 
 #if 0 /* NM_IGNORED */
 bool id128_is_valid(const char *s) {
@@ -24,7 +44,7 @@ bool id128_is_valid(const char *s) {
         l = strlen(s);
 
         if (l == SD_ID128_STRING_MAX - 1)
-                /* Plain formatted 128bit hex string */
+                /* Plain formatted 128-bit hex string */
                 return in_charset(s, HEXDIGITS);
 
         if (l == SD_ID128_UUID_STRING_MAX - 1) {
@@ -53,7 +73,7 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
 
         assert(fd >= 0);
 
-        /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
+        /* Reads an 128-bit 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".
@@ -123,7 +143,7 @@ int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) {
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
         assert(path);
 
-        fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
+        fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
         if (fd < 0)
                 return fd;
 
@@ -151,7 +171,7 @@ int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) {
         }
 
         buffer[sz - 1] = '\n';
-        r = loop_write(fd, buffer, sz, false);
+        r = loop_write(fd, buffer, sz);
         if (r < 0)
                 return r;
 
@@ -170,7 +190,7 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
         assert(path);
 
-        fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
+        fd = xopenat_full(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
         if (fd < 0)
                 return fd;
 
@@ -178,11 +198,11 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
 }
 
 void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(sd_id128_t), state);
+        siphash24_compress_typesafe(*p, state);
 }
 
 int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
-        return memcmp(a, b, 16);
+        return memcmp(a, b, sizeof(sd_id128_t));
 }
 
 sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
@@ -210,9 +230,22 @@ int id128_get_product(sd_id128_t *ret) {
         /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
          * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
 
-        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
-        if (r == -ENOENT)
-                r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
+        r = detect_container();
+        if (r < 0)
+                return r;
+        if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but
+                    * of the host */
+                return -ENOENT;
+
+        FOREACH_STRING(i,
+                       "/sys/class/dmi/id/product_uuid", /* KVM */
+                       "/proc/device-tree/vm,uuid",      /* Device tree */
+                       "/sys/hypervisor/uuid") {         /* Xen */
+
+                r = id128_read(i, ID128_FORMAT_UUID, &uuid);
+                if (r != -ENOENT)
+                        break;
+        }
         if (r < 0)
                 return r;
 
@@ -222,4 +255,22 @@ int id128_get_product(sd_id128_t *ret) {
         *ret = uuid;
         return 0;
 }
+
+sd_id128_t id128_digest(const void *data, size_t size) {
+        assert(data || size == 0);
+
+        /* Hashes a UUID from some arbitrary data */
+
+        if (size == SIZE_MAX)
+                size = strlen(data);
+
+        uint8_t h[SHA256_DIGEST_SIZE];
+        sd_id128_t id;
+
+        /* Take the first half of the SHA256 result */
+        assert_cc(sizeof(h) >= sizeof(id.bytes));
+        memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes));
+
+        return id128_make_v4_uuid(id);
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
index 7bcbd8e5..53ba50a8 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
@@ -6,6 +6,7 @@
 
 #include "sd-id128.h"
 
+#include "errno-util.h"
 #include "hash-funcs.h"
 #include "macro.h"
 
@@ -20,6 +21,8 @@ typedef enum Id128Flag {
         ID128_REFUSE_NULL   = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */
 } Id128Flag;
 
+int id128_from_string_nonzero(const char *s, sd_id128_t *ret);
+
 int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret);
 int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret);
 static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) {
@@ -44,9 +47,12 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id);
 
 int id128_get_product(sd_id128_t *ret);
 
+sd_id128_t id128_digest(const void *data, size_t size);
+
 /* A helper to check for the three relevant cases of "machine ID not initialized" */
-#define ERRNO_IS_MACHINE_ID_UNSET(r)            \
-        IN_SET(abs(r),                          \
-               ENOENT,                          \
-               ENOMEDIUM,                       \
-               ENOPKG)
+#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r)        \
+        IN_SET(r,                               \
+               -ENOENT,                         \
+               -ENOMEDIUM,                      \
+               -ENOPKG)
+_DEFINE_ABS_WRAPPER(MACHINE_ID_UNSET);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
index c63e1a97..ff0db776 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
@@ -345,18 +345,20 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
         return 0;
 }
 
-static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
-        uint8_t hmac[SHA256_DIGEST_SIZE];
-        sd_id128_t result;
+_public_ int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
+        assert_cc(sizeof(sd_id128_t) < SHA256_DIGEST_SIZE); /* Check that we don't need to pad with zeros. */
+        union {
+                uint8_t hmac[SHA256_DIGEST_SIZE];
+                sd_id128_t result;
+        } buf;
 
-        assert(ret);
+        assert_return(ret, -EINVAL);
+        assert_return(!sd_id128_is_null(app_id), -ENXIO);
 
-        hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
+        hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), buf.hmac);
 
         /* Take only the first half. */
-        memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
-
-        *ret = id128_make_v4_uuid(result);
+        *ret = id128_make_v4_uuid(buf.result);
         return 0;
 }
 
@@ -370,7 +372,7 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re
         if (r < 0)
                 return r;
 
-        return get_app_specific(id, app_id, ret);
+        return sd_id128_get_app_specific(id, app_id, ret);
 }
 
 _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
@@ -383,6 +385,6 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
         if (r < 0)
                 return r;
 
-        return get_app_specific(id, app_id, ret);
+        return sd_id128_get_app_specific(id, app_id, ret);
 }
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h
index 6f657c22..d4381d90 100644
--- a/src/libnm-systemd-core/src/systemd/_sd-common.h
+++ b/src/libnm-systemd-core/src/systemd/_sd-common.h
@@ -99,7 +99,7 @@ typedef void (*_sd_destroy_t)(void *userdata);
         }                                                       \
         struct _sd_useless_struct_to_allow_trailing_semicolon_
 
-/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can
+/* The following macro should be used in all public enums, to force 64-bit wideness on them, so that we can
  * freely extend them later on, without breaking compatibility. */
 #define _SD_ENUM_FORCE_S64(id)               \
         _SD_##id##_INT64_MIN = INT64_MIN,    \
diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h
index e3d647f7..b67ec0f3 100644
--- a/src/libnm-systemd-core/src/systemd/sd-device.h
+++ b/src/libnm-systemd-core/src/systemd/sd-device.h
@@ -129,6 +129,7 @@ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumera
 int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match);
 int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match);
 int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value);
+int sd_device_enumerator_add_match_property_required(sd_device_enumerator *enumerator, const char *property, const char *value);
 int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname);
 int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname);
 int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h
new file mode 100644
index 00000000..555b40e9
--- /dev/null
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpduidhfoo
+#define foosddhcpduidhfoo
+
+/***
+  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 <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_DUID_TYPE_LLT        = 1,
+        SD_DUID_TYPE_EN         = 2,
+        SD_DUID_TYPE_LL         = 3,
+        SD_DUID_TYPE_UUID       = 4
+};
+
+typedef struct sd_dhcp_duid sd_dhcp_duid;
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size);
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size);
+
+int sd_dhcp_duid_set(
+                sd_dhcp_duid *duid,
+                uint16_t duid_type,
+                const void *data,
+                size_t data_size);
+int sd_dhcp_duid_set_raw(
+                sd_dhcp_duid *duid,
+                const void *data,
+                size_t data_size);
+int sd_dhcp_duid_set_llt(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type,
+                uint64_t usec);
+int sd_dhcp_duid_set_ll(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type);
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid);
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
index a9fa7856..d551b4dd 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 
 #include "sd-device.h"
+#include "sd-dhcp-duid.h"
 #include "sd-dhcp6-lease.h"
 #include "sd-dhcp6-option.h"
 #include "sd-event.h"
@@ -40,154 +41,6 @@ enum {
         SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST       = 13
 };
 
-/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
-enum {
-        SD_DHCP6_OPTION_CLIENTID                   = 1,  /* RFC 8415 */
-        SD_DHCP6_OPTION_SERVERID                   = 2,  /* RFC 8415 */
-        SD_DHCP6_OPTION_IA_NA                      = 3,  /* RFC 8415 */
-        SD_DHCP6_OPTION_IA_TA                      = 4,  /* RFC 8415 */
-        SD_DHCP6_OPTION_IAADDR                     = 5,  /* RFC 8415 */
-        SD_DHCP6_OPTION_ORO                        = 6,  /* RFC 8415 */
-        SD_DHCP6_OPTION_PREFERENCE                 = 7,  /* RFC 8415 */
-        SD_DHCP6_OPTION_ELAPSED_TIME               = 8,  /* RFC 8415 */
-        SD_DHCP6_OPTION_RELAY_MSG                  = 9,  /* RFC 8415 */
-        /* option code 10 is unassigned */
-        SD_DHCP6_OPTION_AUTH                       = 11,  /* RFC 8415 */
-        SD_DHCP6_OPTION_UNICAST                    = 12,  /* RFC 8415 */
-        SD_DHCP6_OPTION_STATUS_CODE                = 13,  /* RFC 8415 */
-        SD_DHCP6_OPTION_RAPID_COMMIT               = 14,  /* RFC 8415 */
-        SD_DHCP6_OPTION_USER_CLASS                 = 15,  /* RFC 8415 */
-        SD_DHCP6_OPTION_VENDOR_CLASS               = 16,  /* RFC 8415 */
-        SD_DHCP6_OPTION_VENDOR_OPTS                = 17,  /* RFC 8415 */
-        SD_DHCP6_OPTION_INTERFACE_ID               = 18,  /* RFC 8415 */
-        SD_DHCP6_OPTION_RECONF_MSG                 = 19,  /* RFC 8415 */
-        SD_DHCP6_OPTION_RECONF_ACCEPT              = 20,  /* RFC 8415 */
-        SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME     = 21,  /* RFC 3319 */
-        SD_DHCP6_OPTION_SIP_SERVER_ADDRESS         = 22,  /* RFC 3319 */
-        SD_DHCP6_OPTION_DNS_SERVER                 = 23,  /* RFC 3646 */
-        SD_DHCP6_OPTION_DOMAIN                     = 24,  /* RFC 3646 */
-        SD_DHCP6_OPTION_IA_PD                      = 25,  /* RFC 3633, RFC 8415 */
-        SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, RFC 8415 */
-        SD_DHCP6_OPTION_NIS_SERVER                 = 27,  /* RFC 3898 */
-        SD_DHCP6_OPTION_NISP_SERVER                = 28,  /* RFC 3898 */
-        SD_DHCP6_OPTION_NIS_DOMAIN_NAME            = 29,  /* RFC 3898 */
-        SD_DHCP6_OPTION_NISP_DOMAIN_NAME           = 30,  /* RFC 3898 */
-        SD_DHCP6_OPTION_SNTP_SERVER                = 31,  /* RFC 4075, deprecated */
-        SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME   = 32,  /* RFC 4242, 8415, sec. 21.23 */
-        SD_DHCP6_OPTION_BCMCS_SERVER_D             = 33,  /* RFC 4280 */
-        SD_DHCP6_OPTION_BCMCS_SERVER_A             = 34,  /* RFC 4280 */
-        /* option code 35 is unassigned */
-        SD_DHCP6_OPTION_GEOCONF_CIVIC              = 36,  /* RFC 4776 */
-        SD_DHCP6_OPTION_REMOTE_ID                  = 37,  /* RFC 4649 */
-        SD_DHCP6_OPTION_SUBSCRIBER_ID              = 38,  /* RFC 4580 */
-        SD_DHCP6_OPTION_CLIENT_FQDN                = 39,  /* RFC 4704 */
-        SD_DHCP6_OPTION_PANA_AGENT                 = 40,  /* RFC 5192 */
-        SD_DHCP6_OPTION_POSIX_TIMEZONE             = 41,  /* RFC 4833 */
-        SD_DHCP6_OPTION_TZDB_TIMEZONE              = 42,  /* RFC 4833 */
-        SD_DHCP6_OPTION_ERO                        = 43,  /* RFC 4994 */
-        SD_DHCP6_OPTION_LQ_QUERY                   = 44,  /* RFC 5007 */
-        SD_DHCP6_OPTION_CLIENT_DATA                = 45,  /* RFC 5007 */
-        SD_DHCP6_OPTION_CLT_TIME                   = 46,  /* RFC 5007 */
-        SD_DHCP6_OPTION_LQ_RELAY_DATA              = 47,  /* RFC 5007 */
-        SD_DHCP6_OPTION_LQ_CLIENT_LINK             = 48,  /* RFC 5007 */
-        SD_DHCP6_OPTION_MIP6_HNIDF                 = 49,  /* RFC 6610 */
-        SD_DHCP6_OPTION_MIP6_VDINF                 = 50,  /* RFC 6610 */
-        SD_DHCP6_OPTION_V6_LOST                    = 51,  /* RFC 5223 */
-        SD_DHCP6_OPTION_CAPWAP_AC_V6               = 52,  /* RFC 5417 */
-        SD_DHCP6_OPTION_RELAY_ID                   = 53,  /* RFC 5460 */
-        SD_DHCP6_OPTION_IPV6_ADDRESS_MOS           = 54,  /* RFC 5678 */
-        SD_DHCP6_OPTION_IPV6_FQDN_MOS              = 55,  /* RFC 5678 */
-        SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
-        SD_DHCP6_OPTION_V6_ACCESS_DOMAIN           = 57,  /* RFC 5986 */
-        SD_DHCP6_OPTION_SIP_UA_CS_LIST             = 58,  /* RFC 6011 */
-        SD_DHCP6_OPTION_BOOTFILE_URL               = 59,  /* RFC 5970 */
-        SD_DHCP6_OPTION_BOOTFILE_PARAM             = 60,  /* RFC 5970 */
-        SD_DHCP6_OPTION_CLIENT_ARCH_TYPE           = 61,  /* RFC 5970 */
-        SD_DHCP6_OPTION_NII                        = 62,  /* RFC 5970 */
-        SD_DHCP6_OPTION_GEOLOCATION                = 63,  /* RFC 6225 */
-        SD_DHCP6_OPTION_AFTR_NAME                  = 64,  /* RFC 6334 */
-        SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME      = 65,  /* RFC 6440 */
-        SD_DHCP6_OPTION_RSOO                       = 66,  /* RFC 6422 */
-        SD_DHCP6_OPTION_PD_EXCLUDE                 = 67,  /* RFC 6603 */
-        SD_DHCP6_OPTION_VSS                        = 68,  /* RFC 6607 */
-        SD_DHCP6_OPTION_MIP6_IDINF                 = 69,  /* RFC 6610 */
-        SD_DHCP6_OPTION_MIP6_UDINF                 = 70,  /* RFC 6610 */
-        SD_DHCP6_OPTION_MIP6_HNP                   = 71,  /* RFC 6610 */
-        SD_DHCP6_OPTION_MIP6_HAA                   = 72,  /* RFC 6610 */
-        SD_DHCP6_OPTION_MIP6_HAF                   = 73,  /* RFC 6610 */
-        SD_DHCP6_OPTION_RDNSS_SELECTION            = 74,  /* RFC 6731 */
-        SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME         = 75,  /* RFC 6784 */
-        SD_DHCP6_OPTION_KRB_REALM_NAME             = 76,  /* RFC 6784 */
-        SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME     = 77,  /* RFC 6784 */
-        SD_DHCP6_OPTION_KRB_KDC                    = 78,  /* RFC 6784 */
-        SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR      = 79,  /* RFC 6939 */
-        SD_DHCP6_OPTION_LINK_ADDRESS               = 80,  /* RFC 6977 */
-        SD_DHCP6_OPTION_RADIUS                     = 81,  /* RFC 7037 */
-        SD_DHCP6_OPTION_SOL_MAX_RT                 = 82,  /* RFC 7083, RFC 8415 */
-        SD_DHCP6_OPTION_INF_MAX_RT                 = 83,  /* RFC 7083, RFC 8415 */
-        SD_DHCP6_OPTION_ADDRSEL                    = 84,  /* RFC 7078 */
-        SD_DHCP6_OPTION_ADDRSEL_TABLE              = 85,  /* RFC 7078 */
-        SD_DHCP6_OPTION_V6_PCP_SERVER              = 86,  /* RFC 7291 */
-        SD_DHCP6_OPTION_DHCPV4_MSG                 = 87,  /* RFC 7341 */
-        SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER       = 88,  /* RFC 7341 */
-        SD_DHCP6_OPTION_S46_RULE                   = 89,  /* RFC 7598 */
-        SD_DHCP6_OPTION_S46_BR                     = 90,  /* RFC 7598, RFC 8539 */
-        SD_DHCP6_OPTION_S46_DMR                    = 91,  /* RFC 7598 */
-        SD_DHCP6_OPTION_S46_V4V6BIND               = 92,  /* RFC 7598 */
-        SD_DHCP6_OPTION_S46_PORTPARAMS             = 93,  /* RFC 7598 */
-        SD_DHCP6_OPTION_S46_CONT_MAPE              = 94,  /* RFC 7598 */
-        SD_DHCP6_OPTION_S46_CONT_MAPT              = 95,  /* RFC 7598 */
-        SD_DHCP6_OPTION_S46_CONT_LW                = 96,  /* RFC 7598 */
-        SD_DHCP6_OPTION_4RD                        = 97,  /* RFC 7600 */
-        SD_DHCP6_OPTION_4RD_MAP_RULE               = 98,  /* RFC 7600 */
-        SD_DHCP6_OPTION_4RD_NON_MAP_RULE           = 99,  /* RFC 7600 */
-        SD_DHCP6_OPTION_LQ_BASE_TIME               = 100, /* RFC 7653 */
-        SD_DHCP6_OPTION_LQ_START_TIME              = 101, /* RFC 7653 */
-        SD_DHCP6_OPTION_LQ_END_TIME                = 102, /* RFC 7653 */
-        SD_DHCP6_OPTION_CAPTIVE_PORTAL             = 103, /* RFC 8910 */
-        SD_DHCP6_OPTION_MPL_PARAMETERS             = 104, /* RFC 7774 */
-        SD_DHCP6_OPTION_ANI_ATT                    = 105, /* RFC 7839 */
-        SD_DHCP6_OPTION_ANI_NETWORK_NAME           = 106, /* RFC 7839 */
-        SD_DHCP6_OPTION_ANI_AP_NAME                = 107, /* RFC 7839 */
-        SD_DHCP6_OPTION_ANI_AP_BSSID               = 108, /* RFC 7839 */
-        SD_DHCP6_OPTION_ANI_OPERATOR_ID            = 109, /* RFC 7839 */
-        SD_DHCP6_OPTION_ANI_OPERATOR_REALM         = 110, /* RFC 7839 */
-        SD_DHCP6_OPTION_S46_PRIORITY               = 111, /* RFC 8026 */
-        SD_DHCP6_OPTION_MUD_URL_V6                 = 112, /* RFC 8520 */
-        SD_DHCP6_OPTION_V6_PREFIX64                = 113, /* RFC 8115 */
-        SD_DHCP6_OPTION_F_BINDING_STATUS           = 114, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_CONNECT_FLAGS            = 115, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO         = 116, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_DNS_HOST_NAME            = 117, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_DNS_ZONE_NAME            = 118, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_DNS_FLAGS                = 119, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_EXPIRATION_TIME          = 120, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD       = 121, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_MCLT                     = 122, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_PARTNER_LIFETIME         = 123, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT    = 124, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME        = 125, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME     = 126, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_PROTOCOL_VERSION         = 127, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_KEEPALIVE_TIME           = 128, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_RECONFIGURE_DATA         = 129, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_RELATIONSHIP_NAME        = 130, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_SERVER_FLAGS             = 131, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_SERVER_STATE             = 132, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_START_TIME_OF_STATE      = 133, /* RFC 8156 */
-        SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME    = 134, /* RFC 8156 */
-        SD_DHCP6_OPTION_RELAY_PORT                 = 135, /* RFC 8357 */
-        SD_DHCP6_OPTION_V6_SZTP_REDIRECT           = 136, /* RFC 8572 */
-        SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX       = 137, /* RFC 8539 */
-        SD_DHCP6_OPTION_IA_LL                      = 138, /* RFC 8947 */
-        SD_DHCP6_OPTION_LLADDR                     = 139, /* RFC 8947 */
-        SD_DHCP6_OPTION_SLAP_QUAD                  = 140, /* RFC 8948 */
-        SD_DHCP6_OPTION_V6_DOTS_RI                 = 141, /* RFC 8973 */
-        SD_DHCP6_OPTION_V6_DOTS_ADDRESS            = 142, /* RFC 8973 */
-        SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF         = 143 /* RFC 6153 */
-        /* 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);
@@ -211,23 +64,20 @@ int sd_dhcp6_client_set_mac(
                 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_duid_llt(sd_dhcp6_client *client, uint64_t llt_time);
+int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
+int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid);
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret);
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret);
 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);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
index 716f6fc1..e18d5781 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
@@ -23,6 +23,8 @@
 #include <netinet/in.h>
 #include <sys/types.h>
 
+#include "sd-dhcp6-option.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
@@ -30,24 +32,54 @@ _SD_BEGIN_DECLARATIONS;
 typedef struct sd_dhcp6_lease sd_dhcp6_lease;
 
 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
+int sd_dhcp6_lease_get_t1_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_t2(sd_dhcp6_lease *lease, uint64_t *ret);
+int sd_dhcp6_lease_get_t2_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_valid_lifetime(sd_dhcp6_lease *lease, uint64_t *ret);
+int sd_dhcp6_lease_get_valid_lifetime_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
 
-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_address_iterator_reset(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_address(
+                sd_dhcp6_lease *lease,
+                struct in6_addr *ret);
+int sd_dhcp6_lease_get_address_lifetime(
+                sd_dhcp6_lease *lease,
+                uint64_t *ret_lifetime_preferred,
+                uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_get_address_lifetime_timestamp(
+                sd_dhcp6_lease *lease,
+                clockid_t clock,
+                uint64_t *ret_lifetime_preferred,
+                uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease);
+
+int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease);
+int sd_dhcp6_lease_get_pd_prefix(
+                sd_dhcp6_lease *lease,
+                struct in6_addr *ret_prefix,
+                uint8_t *ret_prefix_length);
+int sd_dhcp6_lease_get_pd_lifetime(
+                sd_dhcp6_lease *lease,
+                uint64_t *ret_lifetime_preferred,
+                uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_get_pd_lifetime_timestamp(
+                sd_dhcp6_lease *lease,
+                clockid_t clock,
+                uint64_t *ret_lifetime_preferred,
+                uint64_t *ret_lifetime_valid);
+int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
 
 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
+int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret);
+int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret);
 
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
 sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
index b4b4671e..32012426 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
@@ -20,6 +20,8 @@
 #include <inttypes.h>
 #include <sys/types.h>
 
+#include "sd-dhcp6-protocol.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
new file mode 100644
index 00000000..78c80f7c
--- /dev/null
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcp6protocolhfoo
+#define foosddhcp6protocolhfoo
+
+/***
+  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 <https://www.gnu.org/licenses/>.
+***/
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
+enum {
+        SD_DHCP6_OPTION_CLIENTID                   = 1,  /* RFC 8415 */
+        SD_DHCP6_OPTION_SERVERID                   = 2,  /* RFC 8415 */
+        SD_DHCP6_OPTION_IA_NA                      = 3,  /* RFC 8415 */
+        SD_DHCP6_OPTION_IA_TA                      = 4,  /* RFC 8415 */
+        SD_DHCP6_OPTION_IAADDR                     = 5,  /* RFC 8415 */
+        SD_DHCP6_OPTION_ORO                        = 6,  /* RFC 8415 */
+        SD_DHCP6_OPTION_PREFERENCE                 = 7,  /* RFC 8415 */
+        SD_DHCP6_OPTION_ELAPSED_TIME               = 8,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RELAY_MSG                  = 9,  /* RFC 8415 */
+        /* option code 10 is unassigned */
+        SD_DHCP6_OPTION_AUTH                       = 11,  /* RFC 8415 */
+        SD_DHCP6_OPTION_UNICAST                    = 12,  /* RFC 8415 */
+        SD_DHCP6_OPTION_STATUS_CODE                = 13,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RAPID_COMMIT               = 14,  /* RFC 8415 */
+        SD_DHCP6_OPTION_USER_CLASS                 = 15,  /* RFC 8415 */
+        SD_DHCP6_OPTION_VENDOR_CLASS               = 16,  /* RFC 8415 */
+        SD_DHCP6_OPTION_VENDOR_OPTS                = 17,  /* RFC 8415 */
+        SD_DHCP6_OPTION_INTERFACE_ID               = 18,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RECONF_MSG                 = 19,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RECONF_ACCEPT              = 20,  /* RFC 8415 */
+        SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME     = 21,  /* RFC 3319 */
+        SD_DHCP6_OPTION_SIP_SERVER_ADDRESS         = 22,  /* RFC 3319 */
+        SD_DHCP6_OPTION_DNS_SERVER                 = 23,  /* RFC 3646 */
+        SD_DHCP6_OPTION_DOMAIN                     = 24,  /* RFC 3646 */
+        SD_DHCP6_OPTION_IA_PD                      = 25,  /* RFC 3633, RFC 8415 */
+        SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, RFC 8415 */
+        SD_DHCP6_OPTION_NIS_SERVER                 = 27,  /* RFC 3898 */
+        SD_DHCP6_OPTION_NISP_SERVER                = 28,  /* RFC 3898 */
+        SD_DHCP6_OPTION_NIS_DOMAIN_NAME            = 29,  /* RFC 3898 */
+        SD_DHCP6_OPTION_NISP_DOMAIN_NAME           = 30,  /* RFC 3898 */
+        SD_DHCP6_OPTION_SNTP_SERVER                = 31,  /* RFC 4075, deprecated */
+        SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME   = 32,  /* RFC 4242, 8415, sec. 21.23 */
+        SD_DHCP6_OPTION_BCMCS_SERVER_D             = 33,  /* RFC 4280 */
+        SD_DHCP6_OPTION_BCMCS_SERVER_A             = 34,  /* RFC 4280 */
+        /* option code 35 is unassigned */
+        SD_DHCP6_OPTION_GEOCONF_CIVIC              = 36,  /* RFC 4776 */
+        SD_DHCP6_OPTION_REMOTE_ID                  = 37,  /* RFC 4649 */
+        SD_DHCP6_OPTION_SUBSCRIBER_ID              = 38,  /* RFC 4580 */
+        SD_DHCP6_OPTION_CLIENT_FQDN                = 39,  /* RFC 4704 */
+        SD_DHCP6_OPTION_PANA_AGENT                 = 40,  /* RFC 5192 */
+        SD_DHCP6_OPTION_POSIX_TIMEZONE             = 41,  /* RFC 4833 */
+        SD_DHCP6_OPTION_TZDB_TIMEZONE              = 42,  /* RFC 4833 */
+        SD_DHCP6_OPTION_ERO                        = 43,  /* RFC 4994 */
+        SD_DHCP6_OPTION_LQ_QUERY                   = 44,  /* RFC 5007 */
+        SD_DHCP6_OPTION_CLIENT_DATA                = 45,  /* RFC 5007 */
+        SD_DHCP6_OPTION_CLT_TIME                   = 46,  /* RFC 5007 */
+        SD_DHCP6_OPTION_LQ_RELAY_DATA              = 47,  /* RFC 5007 */
+        SD_DHCP6_OPTION_LQ_CLIENT_LINK             = 48,  /* RFC 5007 */
+        SD_DHCP6_OPTION_MIP6_HNIDF                 = 49,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_VDINF                 = 50,  /* RFC 6610 */
+        SD_DHCP6_OPTION_V6_LOST                    = 51,  /* RFC 5223 */
+        SD_DHCP6_OPTION_CAPWAP_AC_V6               = 52,  /* RFC 5417 */
+        SD_DHCP6_OPTION_RELAY_ID                   = 53,  /* RFC 5460 */
+        SD_DHCP6_OPTION_IPV6_ADDRESS_MOS           = 54,  /* RFC 5678 */
+        SD_DHCP6_OPTION_IPV6_FQDN_MOS              = 55,  /* RFC 5678 */
+        SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
+        SD_DHCP6_OPTION_V6_ACCESS_DOMAIN           = 57,  /* RFC 5986 */
+        SD_DHCP6_OPTION_SIP_UA_CS_LIST             = 58,  /* RFC 6011 */
+        SD_DHCP6_OPTION_BOOTFILE_URL               = 59,  /* RFC 5970 */
+        SD_DHCP6_OPTION_BOOTFILE_PARAM             = 60,  /* RFC 5970 */
+        SD_DHCP6_OPTION_CLIENT_ARCH_TYPE           = 61,  /* RFC 5970 */
+        SD_DHCP6_OPTION_NII                        = 62,  /* RFC 5970 */
+        SD_DHCP6_OPTION_GEOLOCATION                = 63,  /* RFC 6225 */
+        SD_DHCP6_OPTION_AFTR_NAME                  = 64,  /* RFC 6334 */
+        SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME      = 65,  /* RFC 6440 */
+        SD_DHCP6_OPTION_RSOO                       = 66,  /* RFC 6422 */
+        SD_DHCP6_OPTION_PD_EXCLUDE                 = 67,  /* RFC 6603 */
+        SD_DHCP6_OPTION_VSS                        = 68,  /* RFC 6607 */
+        SD_DHCP6_OPTION_MIP6_IDINF                 = 69,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_UDINF                 = 70,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_HNP                   = 71,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_HAA                   = 72,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_HAF                   = 73,  /* RFC 6610 */
+        SD_DHCP6_OPTION_RDNSS_SELECTION            = 74,  /* RFC 6731 */
+        SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME         = 75,  /* RFC 6784 */
+        SD_DHCP6_OPTION_KRB_REALM_NAME             = 76,  /* RFC 6784 */
+        SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME     = 77,  /* RFC 6784 */
+        SD_DHCP6_OPTION_KRB_KDC                    = 78,  /* RFC 6784 */
+        SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR      = 79,  /* RFC 6939 */
+        SD_DHCP6_OPTION_LINK_ADDRESS               = 80,  /* RFC 6977 */
+        SD_DHCP6_OPTION_RADIUS                     = 81,  /* RFC 7037 */
+        SD_DHCP6_OPTION_SOL_MAX_RT                 = 82,  /* RFC 7083, RFC 8415 */
+        SD_DHCP6_OPTION_INF_MAX_RT                 = 83,  /* RFC 7083, RFC 8415 */
+        SD_DHCP6_OPTION_ADDRSEL                    = 84,  /* RFC 7078 */
+        SD_DHCP6_OPTION_ADDRSEL_TABLE              = 85,  /* RFC 7078 */
+        SD_DHCP6_OPTION_V6_PCP_SERVER              = 86,  /* RFC 7291 */
+        SD_DHCP6_OPTION_DHCPV4_MSG                 = 87,  /* RFC 7341 */
+        SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER       = 88,  /* RFC 7341 */
+        SD_DHCP6_OPTION_S46_RULE                   = 89,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_BR                     = 90,  /* RFC 7598, RFC 8539 */
+        SD_DHCP6_OPTION_S46_DMR                    = 91,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_V4V6BIND               = 92,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_PORTPARAMS             = 93,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_CONT_MAPE              = 94,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_CONT_MAPT              = 95,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_CONT_LW                = 96,  /* RFC 7598 */
+        SD_DHCP6_OPTION_4RD                        = 97,  /* RFC 7600 */
+        SD_DHCP6_OPTION_4RD_MAP_RULE               = 98,  /* RFC 7600 */
+        SD_DHCP6_OPTION_4RD_NON_MAP_RULE           = 99,  /* RFC 7600 */
+        SD_DHCP6_OPTION_LQ_BASE_TIME               = 100, /* RFC 7653 */
+        SD_DHCP6_OPTION_LQ_START_TIME              = 101, /* RFC 7653 */
+        SD_DHCP6_OPTION_LQ_END_TIME                = 102, /* RFC 7653 */
+        SD_DHCP6_OPTION_CAPTIVE_PORTAL             = 103, /* RFC 8910 */
+        SD_DHCP6_OPTION_MPL_PARAMETERS             = 104, /* RFC 7774 */
+        SD_DHCP6_OPTION_ANI_ATT                    = 105, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_NETWORK_NAME           = 106, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_AP_NAME                = 107, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_AP_BSSID               = 108, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_OPERATOR_ID            = 109, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_OPERATOR_REALM         = 110, /* RFC 7839 */
+        SD_DHCP6_OPTION_S46_PRIORITY               = 111, /* RFC 8026 */
+        SD_DHCP6_OPTION_MUD_URL_V6                 = 112, /* RFC 8520 */
+        SD_DHCP6_OPTION_V6_PREFIX64                = 113, /* RFC 8115 */
+        SD_DHCP6_OPTION_F_BINDING_STATUS           = 114, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_CONNECT_FLAGS            = 115, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO         = 116, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_HOST_NAME            = 117, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_ZONE_NAME            = 118, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_FLAGS                = 119, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_EXPIRATION_TIME          = 120, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD       = 121, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_MCLT                     = 122, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_LIFETIME         = 123, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT    = 124, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME        = 125, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME     = 126, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PROTOCOL_VERSION         = 127, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_KEEPALIVE_TIME           = 128, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_RECONFIGURE_DATA         = 129, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_RELATIONSHIP_NAME        = 130, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_SERVER_FLAGS             = 131, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_SERVER_STATE             = 132, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_START_TIME_OF_STATE      = 133, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME    = 134, /* RFC 8156 */
+        SD_DHCP6_OPTION_RELAY_PORT                 = 135, /* RFC 8357 */
+        SD_DHCP6_OPTION_V6_SZTP_REDIRECT           = 136, /* RFC 8572 */
+        SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX       = 137, /* RFC 8539 */
+        SD_DHCP6_OPTION_IA_LL                      = 138, /* RFC 8947 */
+        SD_DHCP6_OPTION_LLADDR                     = 139, /* RFC 8947 */
+        SD_DHCP6_OPTION_SLAP_QUAD                  = 140, /* RFC 8948 */
+        SD_DHCP6_OPTION_V6_DOTS_RI                 = 141, /* RFC 8973 */
+        SD_DHCP6_OPTION_V6_DOTS_ADDRESS            = 142, /* RFC 8973 */
+        SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF         = 143  /* RFC 6153 */
+        /* option codes 144-65535 are unassigned */
+};
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h
index 3303c374..a984a9d8 100644
--- a/src/libnm-systemd-core/src/systemd/sd-id128.h
+++ b/src/libnm-systemd-core/src/systemd/sd-id128.h
@@ -50,6 +50,7 @@ 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_app_specific(sd_id128_t base, sd_id128_t app_id, 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);
 
diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
index ee309a42..5f4f6caf 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
@@ -26,34 +26,14 @@
 #include <sys/types.h>
 
 #include "sd-event.h"
+#include "sd-ndisc-protocol.h"
+#include "sd-ndisc-router.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;
 
 __extension__ typedef enum sd_ndisc_event_t {
         SD_NDISC_EVENT_TIMEOUT,
@@ -63,14 +43,16 @@ __extension__ typedef enum sd_ndisc_event_t {
         _SD_ENUM_FORCE_S64(NDISC_EVENT)
 } sd_ndisc_event_t;
 
-typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata);
+typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, void *message, 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);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
 
 int sd_ndisc_start(sd_ndisc *nd);
 int sd_ndisc_stop(sd_ndisc *nd);
+int sd_ndisc_is_running(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);
@@ -80,52 +62,9 @@ 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_ifname(sd_ndisc *nd, const char *interface_name);
 int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
+int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr);
 int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
 
-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/libnm-systemd-shared/README.md b/src/libnm-systemd-shared/README.md
new file mode 100644
index 00000000..6609df6c
--- /dev/null
+++ b/src/libnm-systemd-shared/README.md
@@ -0,0 +1,57 @@
+libnm-systemd-shared
+====================
+
+This is a fork of systemd source files that are compiled as a static library
+with general purpose helpers.
+
+We mainly need this for [../libnm-systemd-core/](../libnm-systemd-core/), which
+contains builds network tools that we use (our internal DHCPv6 library).
+
+We should not use systemd directly from our sources, beyond what we really need
+to make get libnm-systemd-core working. That means, although the systemd code
+contains many useful utility functions, we should not use them beyond what we
+really need, because one day we want to drop this code again.
+
+
+Reimport Upstream Code
+----------------------
+
+We want to avoid deviations in our fork, and frequently re-import latest
+systemd version (every 4 to 8 weeks). The reason is that we frequently should
+check whether important fixes were done in upstream systemd, and the effort of
+doing that check is half the work of a full reimport.  Also, by reimporting
+frequently, we avoid deviating hugely and fall back too much.
+
+Of course this is cumbersome. We therefore should avoid using the systemd code
+as much as we can, and work towards dropping it altogether.
+
+To do a re-import, do:
+
+- checkout `systemd` branch.
+
+- Use the last commit message (`git commit --allow-empty -C origin/systemd`).
+  Then modify the commit message (`git commit --allow-empty --amend`). The
+  commit message contains a long script that is used to re-import the code.
+  Adjust the script in the commit message, and run it. Commit the changes on
+  `systemd` branch.
+
+- checkout `main` branch, and `git merge systemd`. Fix all issues, test,
+  repeat.
+
+- open merge request, check that all tests pass. In particular, enable build on
+  all test distributions in gitlab-ci.
+
+- push `main` and `systemd` branches. Compare how it was done during past imports.
+
+### Hints
+
+- Eagerly commented out unused functions definitions with `#if 0` and `#endif`.
+- Patching header files is best avoided and keep function declarations.
+- We may create some dummy header files in `src/libnm-systemd-{shared,core}/sd-adapt-\*/`
+  if that is a suitable way to get the code to compile, while having minimal modifications
+  to systemd code.
+- Let git be aware of the merge history. Git can help you to better resolve merge conflicts.
+  For example, a plain rebase of the branch on `main` will result in conflicts. Instead,
+  create a temporary branch and merge the code (no rebase). With the help of the history,
+  git will not run into conflicts during merging. Then, the merged git-tree contains the
+  desired content of the files.
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index b32bd7f6..c93930f3 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -5,6 +5,8 @@ libnm_systemd_shared = static_library(
   sources: files(
     'nm-sd-utils-shared.c',
     'src/basic/alloc-util.c',
+    'src/basic/btrfs.c',
+    'src/basic/devnum-util.c',
     'src/basic/env-file.c',
     'src/basic/env-util.c',
     'src/basic/escape.c',
@@ -58,6 +60,7 @@ libnm_systemd_shared = static_library(
     top_inc,
     src_inc,
   ],
+  c_args: libnm_systemd_common_cflags,
   dependencies: glib_dep,
 )
 
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h b/src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h
index 637892c2..637892c2 100644
--- a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h
+++ b/src/libnm-systemd-shared/sd-adapt-shared/sd-messages.h
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c
index c07ab589..243ff521 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.c
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.c
@@ -105,6 +105,33 @@ void* greedy_realloc0(
         return q;
 }
 
+void* greedy_realloc_append(
+                void **p,
+                size_t *n_p,
+                const void *from,
+                size_t n_from,
+                size_t size) {
+
+        uint8_t *q;
+
+        assert(p);
+        assert(n_p);
+        assert(from || n_from == 0);
+
+        if (n_from > SIZE_MAX - *n_p)
+                return NULL;
+
+        q = greedy_realloc(p, *n_p + n_from, size);
+        if (!q)
+                return NULL;
+
+        memcpy_safe(q + *n_p * size, from, n_from * size);
+
+        *n_p += n_from;
+
+        return q;
+}
+
 void *expand_to_usable(void *ptr, size_t newsize _unused_) {
         return ptr;
 }
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index 9a62381d..c215c33f 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -15,13 +15,12 @@
 
 typedef void (*free_func_t)(void *p);
 typedef void* (*mfree_func_t)(void *p);
-typedef void (*free_array_func_t)(void *p, size_t n);
 
 /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
  * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
 #define ALLOCA_MAX (4U*1024U*1024U)
 
-#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
+#define new(t, n) ((t*) malloc_multiply(n, sizeof(t)))
 
 #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
 
@@ -46,9 +45,9 @@ typedef void (*free_array_func_t)(void *p, size_t n);
                 (t*) alloca0((sizeof(t)*_n_));                          \
         })
 
-#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
 
-#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
+#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t)))
 
 #define malloc0(n) (calloc(1, (n) ?: 1))
 
@@ -113,7 +112,7 @@ static inline bool size_multiply_overflow(size_t size, size_t need) {
         return _unlikely_(need != 0 && size > (SIZE_MAX / need));
 }
 
-_malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
+_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t size) {
         if (size_multiply_overflow(size, need))
                 return NULL;
 
@@ -129,7 +128,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
 }
 #endif
 
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
         if (size_multiply_overflow(size, need))
                 return NULL;
 
@@ -138,7 +137,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si
 
 /* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger
  * than the product of its parameters. */
-static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
+static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) {
         if (size_multiply_overflow(size, need))
                 return NULL;
 
@@ -147,6 +146,7 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
 
 void* greedy_realloc(void **p, size_t need, size_t size);
 void* greedy_realloc0(void **p, size_t need, size_t size);
+void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_from, size_t size);
 
 #define GREEDY_REALLOC(array, need)                                     \
         greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
@@ -154,6 +154,9 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
 #define GREEDY_REALLOC0(array, need)                                    \
         greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
 
+#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from)             \
+        greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
+
 #define alloca0(n)                                      \
         ({                                              \
                 char *_new_;                            \
@@ -224,7 +227,6 @@ static inline size_t malloc_sizeof_safe(void **xp) {
                 MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]),                   \
                 VOID_0))
 
-
 /* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
 #define strdupa_safe(s)                                                 \
         ({                                                              \
@@ -235,7 +237,40 @@ static inline size_t malloc_sizeof_safe(void **xp) {
 #define strndupa_safe(s, n)                                             \
         ({                                                              \
                 const char *_t = (s);                                   \
-                (char*) memdupa_suffix0(_t, strnlen(_t, (n)));          \
+                (char*) memdupa_suffix0(_t, strnlen(_t, n));            \
         })
 
+/* Free every element of the array. */
+static inline void free_many(void **p, size_t n) {
+        assert(p || n == 0);
+
+        FOREACH_ARRAY(i, p, n)
+                *i = mfree(*i);
+}
+
+/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
+static inline void free_many_charp(char **c, size_t n) {
+        free_many((void**) c, n);
+}
+
+_alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
+        size_t old_size;
+        void *q;
+
+        /* Like realloc(), but initializes anything appended to zero */
+
+        old_size = MALLOC_SIZEOF_SAFE(p);
+
+        q = realloc(p, new_size);
+        if (!q)
+                return NULL;
+
+        new_size = MALLOC_SIZEOF_SAFE(q); /* Update with actually allocated space */
+
+        if (new_size > old_size)
+                memset((uint8_t*) q + old_size, 0, new_size - old_size);
+
+        return q;
+}
+
 #include "memory-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/arphrd-util.h b/src/libnm-systemd-shared/src/basic/arphrd-util.h
new file mode 100644
index 00000000..33f5694a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/arphrd-util.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stddef.h>
+
+const char *arphrd_to_name(int id);
+int arphrd_from_name(const char *name);
+
+size_t arphrd_to_hw_addr_len(uint16_t arphrd);
diff --git a/src/libnm-systemd-shared/src/basic/async.h b/src/libnm-systemd-shared/src/basic/async.h
deleted file mode 100644
index e0bbaa56..00000000
--- a/src/libnm-systemd-shared/src/basic/async.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <sys/types.h>
-
-#include "macro.h"
-
-int asynchronous_job(void* (*func)(void *p), void *arg);
-
-int asynchronous_sync(pid_t *ret_pid);
-int asynchronous_close(int fd);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close);
diff --git a/src/libnm-systemd-shared/src/basic/btrfs.c b/src/libnm-systemd-shared/src/basic/btrfs.c
new file mode 100644
index 00000000..3b236078
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <linux/btrfs.h>
+#include <sys/ioctl.h>
+
+#include "btrfs.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "path-util.h"
+
+int btrfs_validate_subvolume_name(const char *name) {
+
+        if (!filename_is_valid(name))
+                return -EINVAL;
+
+        if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
+                return -E2BIG;
+
+        return 0;
+}
+
+static int extract_subvolume_name(const char *path, char **ret) {
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        assert(path);
+        assert(ret);
+
+        r = path_extract_filename(path, &fn);
+        if (r < 0)
+                return r;
+
+        r = btrfs_validate_subvolume_name(fn);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(fn);
+        return 0;
+}
+
+int btrfs_subvol_make(int dir_fd, const char *path) {
+        struct btrfs_ioctl_vol_args args = {};
+        _cleanup_free_ char *subvolume = NULL, *parent = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+        assert(!isempty(path));
+
+        r = extract_subvolume_name(path, &subvolume);
+        if (r < 0)
+                return r;
+
+        r = path_extract_directory(path, &parent);
+        if (r < 0) {
+                if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
+                        return r;
+
+                dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
+                if (dir_fd < 0)
+                        return dir_fd;
+        } else {
+                fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
+                if (fd < 0)
+                        return -errno;
+
+                dir_fd = fd;
+        }
+
+        strncpy(args.name, subvolume, sizeof(args.name)-1);
+
+        return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
+}
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
+        mode_t old, combined;
+        int r;
+
+        assert(path);
+
+        /* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
+        old = umask(~mode);
+        combined = old | ~mode;
+        if (combined != ~mode)
+                umask(combined);
+        r = btrfs_subvol_make(dir_fd, path);
+        umask(old);
+
+        if (r >= 0)
+                return 1; /* subvol worked */
+        if (!ERRNO_IS_NOT_SUPPORTED(r))
+                return r;
+
+        if (mkdirat(dir_fd, path, mode) < 0)
+                return -errno;
+
+        return 0; /* plain directory */
+}
diff --git a/src/libnm-systemd-shared/src/basic/btrfs.h b/src/libnm-systemd-shared/src/basic/btrfs.h
new file mode 100644
index 00000000..38be9d2b
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/btrfs.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+
+int btrfs_validate_subvolume_name(const char *name);
+
+int btrfs_subvol_make(int dir_fd, const char *path);
+
+int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode);
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index 9b30ae03..244f3b65 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 
 #include "constants.h"
+#include "pidref.h"
 #include "set.h"
 
 #define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
@@ -35,7 +36,7 @@ typedef enum CGroupController {
         CGROUP_CONTROLLER_BPF_SOCKET_BIND,
         CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
         /* The BPF hook implementing RestrictFileSystems= is not defined here.
-         * It's applied as late as possible in exec_child() so we don't block
+         * It's applied as late as possible in exec_invoke() so we don't block
          * our own unit setup code. */
 
         _CGROUP_CONTROLLER_MAX,
@@ -66,10 +67,13 @@ typedef enum CGroupMask {
         /* All real cgroup v2 controllers */
         CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
 
+        /* All controllers we want to delegate in case of Delegate=yes. Which are pretty much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */
+        CGROUP_MASK_DELEGATE = CGROUP_MASK_V2,
+
         /* All cgroup v2 BPF pseudo-controllers */
         CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
 
-        _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
+        _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1,
 } CGroupMask;
 
 static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) {
@@ -176,13 +180,13 @@ typedef enum CGroupUnified {
  * generate paths with multiple adjacent / removed.
  */
 
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
-int cg_read_pid(FILE *f, pid_t *_pid);
-int cg_read_event(const char *controller, const char *path, const char *event,
-                  char **val);
+int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
+int cg_read_pid(FILE *f, pid_t *ret);
+int cg_read_pidref(FILE *f, PidRef *ret);
+int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
 
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
-int cg_read_subgroup(DIR *d, char **fn);
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
+int cg_read_subgroup(DIR *d, char **ret);
 
 typedef enum CGroupFlags {
         CGROUP_SIGCONT     = 1 << 0,
@@ -190,25 +194,31 @@ typedef enum CGroupFlags {
         CGROUP_REMOVE      = 1 << 2,
 } CGroupFlags;
 
-typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
+typedef int (*cg_kill_log_func_t)(const PidRef *pid, int sig, void *userdata);
 
-int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
-int cg_kill_kernel_sigkill(const char *controller, const char *path);
-int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill_kernel_sigkill(const char *path);
+int cg_kill_recursive(const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
 
 int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
-int cg_mangle_path(const char *path, char **result);
+int cg_mangle_path(const char *path, char **ret);
 
-int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
-int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
+int cg_get_path(const char *controller, const char *path, const char *suffix, char **ret);
+int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **ret);
 
-int cg_pid_get_path(const char *controller, pid_t pid, char **path);
+int cg_pid_get_path(const char *controller, pid_t pid, char **ret);
+int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret);
 
 int cg_rmdir(const char *controller, const char *path);
 
-int cg_is_threaded(const char *controller, const char *path);
+int cg_is_threaded(const char *path);
+
+int cg_is_delegated(const char *path);
+int cg_is_delegated_fd(int fd);
+
+int cg_has_coredump_receive(const char *path);
 
-typedef enum  {
+typedef enum {
         CG_KEY_MODE_GRACEFUL = 1 << 0,
 } CGroupKeyMode;
 
@@ -239,14 +249,14 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
 /* Does a parse_boolean() on the attribute contents and sets ret accordingly */
 int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret);
 
-int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid);
+int cg_get_owner(const char *path, uid_t *ret_uid);
 
-int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
-int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
-int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret);
+int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
+int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
+int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
 /* Returns negative on error, and 0 or 1 on success for the bool value */
-int cg_get_xattr_bool(const char *controller, const char *path, const char *name);
-int cg_remove_xattr(const char *controller, const char *path, const char *name);
+int cg_get_xattr_bool(const char *path, const char *name);
+int cg_remove_xattr(const char *path, const char *name);
 
 int cg_install_release_agent(const char *controller, const char *agent);
 int cg_uninstall_release_agent(const char *controller);
@@ -257,27 +267,28 @@ int cg_is_empty_recursive(const char *controller, const char *path);
 int cg_get_root_path(char **path);
 
 int cg_path_get_cgroupid(const char *path, uint64_t *ret);
-int cg_path_get_session(const char *path, char **session);
-int cg_path_get_owner_uid(const char *path, uid_t *uid);
-int cg_path_get_unit(const char *path, char **unit);
-int cg_path_get_unit_path(const char *path, char **unit);
-int cg_path_get_user_unit(const char *path, char **unit);
-int cg_path_get_machine_name(const char *path, char **machine);
-int cg_path_get_slice(const char *path, char **slice);
-int cg_path_get_user_slice(const char *path, char **slice);
-
-int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
-int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
-
-int cg_pid_get_session(pid_t pid, char **session);
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
-int cg_pid_get_unit(pid_t pid, char **unit);
-int cg_pid_get_user_unit(pid_t pid, char **unit);
-int cg_pid_get_machine_name(pid_t pid, char **machine);
-int cg_pid_get_slice(pid_t pid, char **slice);
-int cg_pid_get_user_slice(pid_t pid, char **slice);
-
-int cg_path_decode_unit(const char *cgroup, char **unit);
+int cg_path_get_session(const char *path, char **ret_session);
+int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
+int cg_path_get_unit(const char *path, char **ret_unit);
+int cg_path_get_unit_path(const char *path, char **ret_unit);
+int cg_path_get_user_unit(const char *path, char **ret_unit);
+int cg_path_get_machine_name(const char *path, char **ret_machine);
+int cg_path_get_slice(const char *path, char **ret_slice);
+int cg_path_get_user_slice(const char *path, char **ret_slice);
+
+int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_shifted);
+int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
+
+int cg_pid_get_session(pid_t pid, char **ret_session);
+int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
+int cg_pid_get_unit(pid_t pid, char **ret_unit);
+int cg_pidref_get_unit(const PidRef *pidref, char **ret);
+int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
+int cg_pid_get_machine_name(pid_t pid, char **ret_machine);
+int cg_pid_get_slice(pid_t pid, char **ret_slice);
+int cg_pid_get_user_slice(pid_t pid, char **ret_slice);
+
+int cg_path_decode_unit(const char *cgroup, char **ret_unit);
 
 bool cg_needs_escape(const char *p);
 int cg_escape(const char *p, char **ret);
diff --git a/src/libnm-systemd-shared/src/basic/chase.h b/src/libnm-systemd-shared/src/basic/chase.h
new file mode 100644
index 00000000..cfc714b9
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/chase.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <dirent.h>
+#include <stdio.h>
+
+#include "stat-util.h"
+
+typedef enum ChaseFlags {
+        CHASE_PREFIX_ROOT        = 1 << 0,  /* The specified path will be prefixed by the specified root before beginning the iteration */
+        CHASE_NONEXISTENT        = 1 << 1,  /* It's OK if the path doesn't actually exist. */
+        CHASE_NO_AUTOFS          = 1 << 2,  /* Return -EREMOTE if autofs mount point found */
+        CHASE_SAFE               = 1 << 3,  /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
+        CHASE_TRAIL_SLASH        = 1 << 4,  /* Any trailing slash will be preserved */
+        CHASE_STEP               = 1 << 5,  /* Just execute a single step of the normalization */
+        CHASE_NOFOLLOW           = 1 << 6,  /* Do not follow the path's right-most component. With ret_fd, when the path's
+                                             * right-most component refers to symlink, return O_PATH fd of the symlink. */
+        CHASE_WARN               = 1 << 7,  /* Emit an appropriate warning when an error is encountered.
+                                             * Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
+        CHASE_AT_RESOLVE_IN_ROOT = 1 << 8,  /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
+                                             * relative to the given directory fd instead of root. */
+        CHASE_PROHIBIT_SYMLINKS  = 1 << 9,  /* Refuse all symlinks */
+        CHASE_PARENT             = 1 << 10, /* Chase the parent directory of the given path. Note that the
+                                             * full path is still stored in ret_path and only the returned
+                                             * file descriptor will point to the parent directory. Note that
+                                             * the result path is the root or '.', then the file descriptor
+                                             * also points to the result path even if this flag is set.
+                                             * When this specified, chase() will succeed with 1 even if the
+                                             * file points to the last path component does not exist. */
+        CHASE_MKDIR_0755         = 1 << 11, /* Create any missing parent directories in the given path. This
+                                             * needs to be set with CHASE_NONEXISTENT and/or CHASE_PARENT.
+                                             * Note, chase_and_open() or friends always add CHASE_PARENT flag
+                                             * when internally call chase(), hence CHASE_MKDIR_0755 can be
+                                             * safely set without CHASE_NONEXISTENT and CHASE_PARENT. */
+        CHASE_EXTRACT_FILENAME   = 1 << 12, /* Only return the last component of the resolved path */
+} ChaseFlags;
+
+bool unsafe_transition(const struct stat *a, const struct stat *b);
+
+/* How many iterations to execute before returning -ELOOP */
+#define CHASE_MAX 32
+
+int chase(const char *path_with_prefix, const char *root, ChaseFlags chase_flags, char **ret_path, int *ret_fd);
+
+int chaseat_prefix_root(const char *path, const char *root, char **ret);
+int chase_extract_filename(const char *path, const char *root, char **ret);
+
+int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path);
+int chase_and_opendir(const char *path, const char *root, ChaseFlags chase_flags, char **ret_path, DIR **ret_dir);
+int chase_and_stat(const char *path, const char *root, ChaseFlags chase_flags, char **ret_path, struct stat *ret_stat);
+int chase_and_access(const char *path, const char *root, ChaseFlags chase_flags, int access_mode, char **ret_path);
+int chase_and_fopen_unlocked(const char *path, const char *root, ChaseFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
+int chase_and_unlink(const char *path, const char *root, ChaseFlags chase_flags, int unlink_flags, char **ret_path);
+int chase_and_open_parent(const char *path, const char *root, ChaseFlags chase_flags, char **ret_filename);
+
+int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int *ret_fd);
+
+int chase_and_openat(int dir_fd, const char *path, ChaseFlags chase_flags, int open_flags, char **ret_path);
+int chase_and_opendirat(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_path, DIR **ret_dir);
+int chase_and_statat(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_path, struct stat *ret_stat);
+int chase_and_accessat(int dir_fd, const char *path, ChaseFlags chase_flags, int access_mode, char **ret_path);
+int chase_and_fopenat_unlocked(int dir_fd, const char *path, ChaseFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
+int chase_and_unlinkat(int dir_fd, const char *path, ChaseFlags chase_flags, int unlink_flags, char **ret_path);
+int chase_and_open_parent_at(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_filename);
diff --git a/src/libnm-systemd-shared/src/basic/constants.h b/src/libnm-systemd-shared/src/basic/constants.h
index 3f96786d..6bb5f3c2 100644
--- a/src/libnm-systemd-shared/src/basic/constants.h
+++ b/src/libnm-systemd-shared/src/basic/constants.h
@@ -59,22 +59,13 @@
 #define NOTIFY_FD_MAX 768
 #define NOTIFY_BUFFER_MAX PIPE_BUF
 
-#if HAVE_SPLIT_USR
-#  define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0"
-#  define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n
-#else
-#  define _CONF_PATHS_SPLIT_USR_NULSTR(n)
-#  define _CONF_PATHS_SPLIT_USR(n)
-#endif
-
 /* Return a nulstr for a standard cascade of configuration paths, suitable to pass to
  * conf_files_list_nulstr() to implement drop-in directories for extending configuration files. */
 #define CONF_PATHS_NULSTR(n)                    \
         "/etc/" n "\0"                          \
         "/run/" n "\0"                          \
         "/usr/local/lib/" n "\0"                \
-        "/usr/lib/" n "\0"                      \
-        _CONF_PATHS_SPLIT_USR_NULSTR(n)
+        "/usr/lib/" n "\0"
 
 #define CONF_PATHS_USR(n)                       \
         "/etc/" n,                              \
@@ -83,8 +74,7 @@
         "/usr/lib/" n
 
 #define CONF_PATHS(n)                           \
-        CONF_PATHS_USR(n)                       \
-        _CONF_PATHS_SPLIT_USR(n)
+        CONF_PATHS_USR(n)
 
 #define CONF_PATHS_USR_STRV(n)                  \
         STRV_MAKE(CONF_PATHS_USR(n))
@@ -99,14 +89,9 @@
  * in containers so that our children inherit that. */
 #define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL)
 
-#define PLYMOUTH_SOCKET {                                       \
-                .un.sun_family = AF_UNIX,                       \
-                .un.sun_path = "\0/org/freedesktop/plymouthd",  \
-        }
-
 /* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */
-#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM"
+#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.systemd.ManagedOOM"
 /* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
-#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
+#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.systemd.ManagedOOM"
 
 #define KERNEL_BASELINE_VERSION "4.15"
diff --git a/src/libnm-systemd-shared/src/basic/devnum-util.c b/src/libnm-systemd-shared/src/basic/devnum-util.c
new file mode 100644
index 00000000..b13c9fc6
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/devnum-util.c
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <string.h>
+#include <sys/stat.h>
+
+#include "chase.h"
+#include "devnum-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+
+int parse_devnum(const char *s, dev_t *ret) {
+        const char *major;
+        unsigned x, y;
+        size_t n;
+        int r;
+
+        n = strspn(s, DIGITS);
+        if (n == 0)
+                return -EINVAL;
+        if (n > DECIMAL_STR_MAX(dev_t))
+                return -EINVAL;
+        if (s[n] != ':')
+                return -EINVAL;
+
+        major = strndupa_safe(s, n);
+        r = safe_atou(major, &x);
+        if (r < 0)
+                return r;
+
+        r = safe_atou(s + n + 1, &y);
+        if (r < 0)
+                return r;
+
+        if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
+                return -ERANGE;
+
+        *ret = makedev(x, y);
+        return 0;
+}
+
+int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
+        const char *t;
+
+        /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
+
+        if (S_ISCHR(mode))
+                t = "char";
+        else if (S_ISBLK(mode))
+                t = "block";
+        else
+                return -ENODEV;
+
+        if (asprintf(ret, "/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
+                return -ENOMEM;
+
+        return 0;
+}
+
+int device_path_make_inaccessible(mode_t mode, char **ret) {
+        char *s;
+
+        assert(ret);
+
+        if (S_ISCHR(mode))
+                s = strdup("/run/systemd/inaccessible/chr");
+        else if (S_ISBLK(mode))
+                s = strdup("/run/systemd/inaccessible/blk");
+        else
+                return -ENODEV;
+        if (!s)
+                return -ENOMEM;
+
+        *ret = s;
+        return 0;
+}
+
+#if 0 /* NM_IGNORED */
+int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
+
+        assert(ret);
+
+        if (devnum_is_zero(devnum))
+                /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
+                 * /dev/block/ and /dev/char/, hence we handle them specially here. */
+                return device_path_make_inaccessible(mode, ret);
+
+        r = device_path_make_major_minor(mode, devnum, &p);
+        if (r < 0)
+                return r;
+
+        return chase(p, NULL, 0, ret, NULL);
+}
+#endif /* NM_IGNORED */
+
+int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum) {
+        mode_t mode;
+        dev_t devnum;
+        int r;
+
+        /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
+         * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
+         * path cannot be parsed like this.  */
+
+        if (path_equal(path, "/run/systemd/inaccessible/chr")) {
+                mode = S_IFCHR;
+                devnum = makedev(0, 0);
+        } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
+                mode = S_IFBLK;
+                devnum = makedev(0, 0);
+        } else {
+                const char *w;
+
+                w = path_startswith(path, "/dev/block/");
+                if (w)
+                        mode = S_IFBLK;
+                else {
+                        w = path_startswith(path, "/dev/char/");
+                        if (!w)
+                                return -ENODEV;
+
+                        mode = S_IFCHR;
+                }
+
+                r = parse_devnum(w, &devnum);
+                if (r < 0)
+                        return r;
+        }
+
+        if (ret_mode)
+                *ret_mode = mode;
+        if (ret_devnum)
+                *ret_devnum = devnum;
+
+        return 0;
+}
diff --git a/src/libnm-systemd-shared/src/basic/devnum-util.h b/src/libnm-systemd-shared/src/basic/devnum-util.h
new file mode 100644
index 00000000..e109de99
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/devnum-util.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "stdio-util.h"
+
+int parse_devnum(const char *s, dev_t *ret);
+
+/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
+ * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
+ * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
+ * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
+ * such a test would be pointless in such a case.) */
+
+#define DEVICE_MAJOR_VALID(x)                                           \
+        ({                                                              \
+                typeof(x) _x = (x), _y = 0;                             \
+                _x >= _y && _x < (UINT32_C(1) << 12);                   \
+                                                                        \
+        })
+
+#define DEVICE_MINOR_VALID(x)                                           \
+        ({                                                              \
+                typeof(x) _x = (x), _y = 0;                             \
+                _x >= _y && _x < (UINT32_C(1) << 20);                   \
+        })
+
+int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
+int device_path_make_inaccessible(mode_t mode, char **ret);
+int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret);
+int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum);
+
+static inline bool devnum_set_and_equal(dev_t a, dev_t b) {
+        /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
+         * know" and we'll return false */
+        return a == b && a != 0;
+}
+
+/* Maximum string length for a major:minor string. (Note that DECIMAL_STR_MAX includes space for a trailing NUL) */
+#define DEVNUM_STR_MAX (DECIMAL_STR_MAX(dev_t)-1+1+DECIMAL_STR_MAX(dev_t))
+
+#define DEVNUM_FORMAT_STR "%u:%u"
+#define DEVNUM_FORMAT_VAL(d) major(d), minor(d)
+
+static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
+        return ASSERT_PTR(snprintf_ok(buf, DEVNUM_STR_MAX, DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(d)));
+}
+
+#define FORMAT_DEVNUM(d) format_devnum((d), (char[DEVNUM_STR_MAX]) {})
+
+static inline bool devnum_is_zero(dev_t d) {
+        return major(d) == 0 && minor(d) == 0;
+}
diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c
index db270bed..75b2febf 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.c
+++ b/src/libnm-systemd-shared/src/basic/env-file.c
@@ -127,7 +127,7 @@ static int parse_env_file_internal(
                                 state = VALUE;
 
                                 if (!GREEDY_REALLOC(value, n_value+2))
-                                        return  -ENOMEM;
+                                        return -ENOMEM;
 
                                 value[n_value++] = c;
                         }
@@ -245,7 +245,13 @@ static int parse_env_file_internal(
                         break;
 
                 case COMMENT_ESCAPE:
-                        state = COMMENT;
+                        log_debug("The line which doesn't begin with \";\" or \"#\", but follows a comment" \
+                                  " line trailing with escape is now treated as a non comment line since v254.");
+                        if (strchr(NEWLINE, c)) {
+                                state = PRE_KEY;
+                                line++;
+                        } else
+                                state = COMMENT;
                         break;
                 }
         }
@@ -522,6 +528,7 @@ static int merge_env_file_push(
 
         char ***env = ASSERT_PTR(userdata);
         char *expanded_value;
+        int r;
 
         assert(key);
 
@@ -536,12 +543,12 @@ static int merge_env_file_push(
                 return 0;
         }
 
-        expanded_value = replace_env(value, *env,
-                                     REPLACE_ENV_USE_ENVIRONMENT|
-                                     REPLACE_ENV_ALLOW_BRACELESS|
-                                     REPLACE_ENV_ALLOW_EXTENDED);
-        if (!expanded_value)
-                return -ENOMEM;
+        r = replace_env(value,
+                        *env,
+                        REPLACE_ENV_USE_ENVIRONMENT|REPLACE_ENV_ALLOW_BRACELESS|REPLACE_ENV_ALLOW_EXTENDED,
+                        &expanded_value);
+        if (r < 0)
+                return log_error_errno(r, "%s:%u: Failed to expand variable '%s': %m", strna(filename), line, value);
 
         free_and_replace(value, expanded_value);
 
@@ -599,7 +606,7 @@ static void write_env_var(FILE *f, const char *v) {
         fputc_unlocked('\n', f);
 }
 
-int write_env_file_at(int dir_fd, const char *fname, char **l) {
+int write_env_file(int dir_fd, const char *fname, char **headers, char **l) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *p = NULL;
         int r;
@@ -613,6 +620,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
 
         (void) fchmod_umask(fileno(f), 0644);
 
+        STRV_FOREACH(i, headers) {
+                assert(isempty(*i) || startswith(*i, "#"));
+                fputs_unlocked(*i, f);
+                fputc_unlocked('\n', f);
+        }
+
         STRV_FOREACH(i, l)
                 write_env_var(f, *i);
 
@@ -627,4 +640,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
         (void) unlinkat(dir_fd, p, 0);
         return r;
 }
+
+int write_vconsole_conf(int dir_fd, const char *fname, char **l) {
+        char **headers = STRV_MAKE(
+                "# Written by systemd-localed(8) or systemd-firstboot(1), read by systemd-localed",
+                "# and systemd-vconsole-setup(8). Use localectl(1) to update this file.");
+
+        return write_env_file(dir_fd, fname, headers, l);
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-file.h b/src/libnm-systemd-shared/src/basic/env-file.h
index 2465eedd..37db3076 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.h
+++ b/src/libnm-systemd-shared/src/basic/env-file.h
@@ -19,7 +19,6 @@ int load_env_file_pairs_fd(int fd, const char *fname, char ***ret);
 
 int merge_env_file(char ***env, FILE *f, const char *fname);
 
-int write_env_file_at(int dir_fd, const char *fname, char **l);
-static inline int write_env_file(const char *fname, char **l) {
-        return write_env_file_at(AT_FDCWD, fname, l);
-}
+int write_env_file(int dir_fd, const char *fname, char **headers, char **l);
+
+int write_vconsole_conf(int dir_fd, const char *fname, char **l);
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index fa2753bc..54509a55 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -29,20 +29,21 @@
         "_"
 
 static bool env_name_is_valid_n(const char *e, size_t n) {
-        if (!e)
-                return false;
+
+        if (n == SIZE_MAX)
+                n = strlen_ptr(e);
 
         if (n <= 0)
                 return false;
 
+        assert(e);
+
         if (ascii_isdigit(e[0]))
                 return false;
 
-        /* POSIX says the overall size of the environment block cannot
-         * be > ARG_MAX, an individual assignment hence cannot be
-         * either. Discounting the equal sign and trailing NUL this
-         * hence leaves ARG_MAX-2 as longest possible variable
-         * name. */
+        /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
+         * hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as
+         * longest possible variable name. */
         if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
                 return false;
 
@@ -246,9 +247,9 @@ static bool env_match(const char *t, const char *pattern) {
                 return true;
 
         if (!strchr(pattern, '=')) {
-                size_t l = strlen(pattern);
+                t = startswith(t, pattern);
 
-                return strneq(t, pattern, l) && t[l] == '=';
+                return t && *t == '=';
         }
 
         return false;
@@ -311,19 +312,17 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
         return TAKE_PTR(t);
 }
 
-char **strv_env_unset(char **l, const char *p) {
-        char **f, **t;
+char** strv_env_unset(char **l, const char *p) {
+        assert(p);
 
         if (!l)
                 return NULL;
 
-        assert(p);
-
         /* Drops every occurrence of the env var setting p in the
          * string list. Edits in-place. */
 
+        char **f, **t;
         for (f = t = l; *f; f++) {
-
                 if (env_match(*f, p)) {
                         free(*f);
                         continue;
@@ -336,14 +335,13 @@ char **strv_env_unset(char **l, const char *p) {
         return l;
 }
 
-char **strv_env_unset_many(char **l, ...) {
-        char **f, **t;
-
+char** strv_env_unset_many_internal(char **l, ...) {
         if (!l)
                 return NULL;
 
         /* Like strv_env_unset() but applies many at once. Edits in-place. */
 
+        char **f, **t;
         for (f = t = l; *f; f++) {
                 bool found = false;
                 const char *p;
@@ -351,12 +349,11 @@ char **strv_env_unset_many(char **l, ...) {
 
                 va_start(ap, l);
 
-                while ((p = va_arg(ap, const char*))) {
+                while ((p = va_arg(ap, const char*)))
                         if (env_match(*f, p)) {
                                 found = true;
                                 break;
                         }
-                }
 
                 va_end(ap);
 
@@ -460,6 +457,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
         return strv_env_replace_consume(l, p);
 }
 
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+        int r;
+
+        assert(l);
+        assert(key);
+
+        if (!env_name_is_valid(key))
+                return -EINVAL;
+
+        if (!valuef) {
+                strv_env_unset(*l, key);
+                return 0;
+        }
+
+        _cleanup_free_ char *value = NULL;
+        va_list ap;
+        va_start(ap, valuef);
+        r = vasprintf(&value, valuef, ap);
+        va_end(ap);
+        if (r < 0)
+                return -ENOMEM;
+
+        char *p = strjoin(key, "=", value);
+        if (!p)
+                return -ENOMEM;
+
+        return strv_env_replace_consume(l, p);
+}
+
 int _strv_env_assign_many(char ***l, ...) {
         va_list ap;
         int r;
@@ -502,32 +528,31 @@ int _strv_env_assign_many(char ***l, ...) {
         return 0;
 }
 
-char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
         assert(name);
 
+        if (k == SIZE_MAX)
+                k = strlen(name);
         if (k <= 0)
                 return NULL;
 
         STRV_FOREACH_BACKWARDS(i, l)
-                if (strneq(*i, name, k) &&
-                    (*i)[k] == '=')
-                        return *i + k + 1;
+                if (strneq(*i, name, k) && (*i)[k] == '=')
+                        return (char*) *i + k + 1;
 
         if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
                 const char *t;
 
+                /* Safety check that the name is not overly long, before we do a stack allocation */
+                if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
+                        return NULL;
+
                 t = strndupa_safe(name, k);
                 return getenv(t);
         };
 
         return NULL;
 }
-
-char *strv_env_get(char **l, const char *name) {
-        assert(name);
-
-        return strv_env_get_n(l, name, strlen(name), 0);
-}
 #endif /* NM_IGNORED */
 
 char *strv_env_pairs_get(char **l, const char *name) {
@@ -578,7 +603,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
         return e;
 }
 
-char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
+static int strv_extend_with_length(char ***l, const char *s, size_t n) {
+        char *c;
+
+        c = strndup(s, n);
+        if (!c)
+                return -ENOMEM;
+
+        return strv_consume(l, c);
+}
+
+static int strv_env_get_n_validated(
+                char **env,
+                const char *name,
+                size_t l,
+                ReplaceEnvFlags flags,
+                char **ret,              /* points into the env block! do not free! */
+                char ***unset_variables, /* updated in place */
+                char ***bad_variables) { /* ditto */
+
+        char *e;
+        int r;
+
+        assert(l == 0 || name);
+        assert(ret);
+
+        if (env_name_is_valid_n(name, l)) {
+                e = strv_env_get_n(env, name, l, flags);
+                if (!e && unset_variables) {
+                        r = strv_extend_with_length(unset_variables, name, l);
+                        if (r < 0)
+                                return r;
+                }
+        } else {
+                e = NULL; /* Resolve invalid variable names the same way as unset ones */
+
+                if (bad_variables) {
+                        r = strv_extend_with_length(bad_variables, name, l);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret = e;
+        return !!e;
+}
+
+int replace_env_full(
+                const char *format,
+                size_t n,
+                char **env,
+                ReplaceEnvFlags flags,
+                char **ret,
+                char ***ret_unset_variables,
+                char ***ret_bad_variables) {
+
         enum {
                 WORD,
                 CURLY,
@@ -589,15 +668,22 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
                 ALTERNATE_VALUE,
         } state = WORD;
 
+        _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
         const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
-        char *k;
         _cleanup_free_ char *s = NULL;
+        char ***pu, ***pb, *k;
         size_t i, len = 0; /* len is initialized to appease gcc */
-        int nest = 0;
+        int nest = 0, r;
 
         assert(format);
 
-        for (e = format, i = 0; *e && i < n; e ++, i ++)
+        if (n == SIZE_MAX)
+                n = strlen(format);
+
+        pu = ret_unset_variables ? &unset_variables : NULL;
+        pb = ret_bad_variables ? &bad_variables : NULL;
+
+        for (e = format, i = 0; *e && i < n; e++, i++)
                 switch (state) {
 
                 case WORD:
@@ -609,27 +695,28 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
                         if (*e == '{') {
                                 k = strnappend(s, word, e-word-1);
                                 if (!k)
-                                        return NULL;
+                                        return -ENOMEM;
 
                                 free_and_replace(s, k);
 
                                 word = e-1;
                                 state = VARIABLE;
                                 nest++;
+
                         } else if (*e == '$') {
                                 k = strnappend(s, word, e-word);
                                 if (!k)
-                                        return NULL;
+                                        return -ENOMEM;
 
                                 free_and_replace(s, k);
 
                                 word = e+1;
                                 state = WORD;
 
-                        } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
+                        } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
                                 k = strnappend(s, word, e-word-1);
                                 if (!k)
-                                        return NULL;
+                                        return -ENOMEM;
 
                                 free_and_replace(s, k);
 
@@ -642,12 +729,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
 
                 case VARIABLE:
                         if (*e == '}') {
-                                const char *t;
+                                char *t;
 
-                                t = strv_env_get_n(env, word+2, e-word-2, flags);
+                                r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
+                                if (r < 0)
+                                        return r;
 
                                 if (!strextend(&s, t))
-                                        return NULL;
+                                        return -ENOMEM;
 
                                 word = e+1;
                                 state = WORD;
@@ -689,18 +778,37 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
 
                         nest--;
                         if (nest == 0) {
-                                const char *t;
+                                _cleanup_strv_free_ char **u = NULL, **b = NULL;
                                 _cleanup_free_ char *v = NULL;
+                                char *t = NULL;
+
+                                r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
+                                if (r < 0)
+                                        return r;
 
-                                t = strv_env_get_n(env, word+2, len, flags);
+                                if (t && state == ALTERNATE_VALUE) {
+                                        r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+                                        if (r < 0)
+                                                return r;
 
-                                if (t && state == ALTERNATE_VALUE)
-                                        t = v = replace_env_n(test_value, e-test_value, env, flags);
-                                else if (!t && state == DEFAULT_VALUE)
-                                        t = v = replace_env_n(test_value, e-test_value, env, flags);
+                                        t = v;
+                                } else if (!t && state == DEFAULT_VALUE) {
+                                        r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
+                                        if (r < 0)
+                                                return r;
+
+                                        t = v;
+                                }
+
+                                r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+                                if (r < 0)
+                                        return r;
+                                r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
+                                if (r < 0)
+                                        return r;
 
                                 if (!strextend(&s, t))
-                                        return NULL;
+                                        return -ENOMEM;
 
                                 word = e+1;
                                 state = WORD;
@@ -711,12 +819,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
                         assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
 
                         if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
-                                const char *t;
+                                char *t = NULL;
 
-                                t = strv_env_get_n(env, word+1, e-word-1, flags);
+                                r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+                                if (r < 0)
+                                        return r;
 
                                 if (!strextend(&s, t))
-                                        return NULL;
+                                        return -ENOMEM;
 
                                 word = e--;
                                 i--;
@@ -726,58 +836,83 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
                 }
 
         if (state == VARIABLE_RAW) {
-                const char *t;
+                char *t;
 
                 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
 
-                t = strv_env_get_n(env, word+1, e-word-1, flags);
-                return strjoin(s, t);
-        } else
-                return strnappend(s, word, e-word);
+                r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
+                if (r < 0)
+                        return r;
+
+                if (!strextend(&s, t))
+                        return -ENOMEM;
+
+        } else if (!strextendn(&s, word, e-word))
+                return -ENOMEM;
+
+        if (ret_unset_variables)
+                *ret_unset_variables = TAKE_PTR(unset_variables);
+        if (ret_bad_variables)
+                *ret_bad_variables = TAKE_PTR(bad_variables);
+
+        if (ret)
+                *ret = TAKE_PTR(s);
+
+        return 0;
 }
 
-char **replace_env_argv(char **argv, char **env) {
-        _cleanup_strv_free_ char **ret = NULL;
+int replace_env_argv(
+                char **argv,
+                char **env,
+                char ***ret,
+                char ***ret_unset_variables,
+                char ***ret_bad_variables) {
+
+        _cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
         size_t k = 0, l = 0;
+        int r;
 
         l = strv_length(argv);
 
-        ret = new(char*, l+1);
-        if (!ret)
-                return NULL;
+        n = new(char*, l+1);
+        if (!n)
+                return -ENOMEM;
 
         STRV_FOREACH(i, argv) {
+                const char *word = *i;
 
                 /* If $FOO appears as single word, replace it by the split up variable */
-                if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
-                        char *e;
-                        char **w;
+                if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
                         _cleanup_strv_free_ char **m = NULL;
+                        const char *name = word + 1;
+                        char *e, **w;
                         size_t q;
 
-                        e = strv_env_get(env, *i+1);
-                        if (e) {
-                                int r;
-
-                                r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
-                                if (r < 0) {
-                                        ret[k] = NULL;
-                                        return NULL;
-                                }
-                        }
+                        if (env_name_is_valid(name)) {
+                                e = strv_env_get(env, name);
+                                if (e)
+                                        r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
+                                else if (ret_unset_variables)
+                                        r = strv_extend(&unset_variables, name);
+                                else
+                                        r = 0;
+                        } else if (ret_bad_variables)
+                                r = strv_extend(&bad_variables, name);
+                        else
+                                r = 0;
+                        if (r < 0)
+                                return r;
 
                         q = strv_length(m);
                         l = l + q - 1;
 
-                        w = reallocarray(ret, l + 1, sizeof(char *));
-                        if (!w) {
-                                ret[k] = NULL;
-                                return NULL;
-                        }
+                        w = reallocarray(n, l + 1, sizeof(char*));
+                        if (!w)
+                                return -ENOMEM;
 
-                        ret = w;
+                        n = w;
                         if (m) {
-                                memcpy(ret + k, m, q * sizeof(char*));
+                                memcpy(n + k, m, (q + 1) * sizeof(char*));
                                 m = mfree(m);
                         }
 
@@ -785,15 +920,41 @@ char **replace_env_argv(char **argv, char **env) {
                         continue;
                 }
 
+                _cleanup_strv_free_ char **u = NULL, **b = NULL;
+
                 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
-                ret[k] = replace_env(*i, env, 0);
-                if (!ret[k])
-                        return NULL;
-                k++;
+                r = replace_env_full(
+                                word,
+                                /* length= */ SIZE_MAX,
+                                env,
+                                /* flags= */ 0,
+                                n + k,
+                                ret_unset_variables ? &u : NULL,
+                                ret_bad_variables ? &b : NULL);
+                if (r < 0)
+                        return r;
+                n[++k] = NULL;
+
+                r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
+                if (r < 0)
+                        return r;
+
+                r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
+                if (r < 0)
+                        return r;
         }
 
-        ret[k] = NULL;
-        return TAKE_PTR(ret);
+        if (ret_unset_variables) {
+                strv_uniq(strv_sort(unset_variables));
+                *ret_unset_variables = TAKE_PTR(unset_variables);
+        }
+        if (ret_bad_variables) {
+                strv_uniq(strv_sort(bad_variables));
+                *ret_bad_variables = TAKE_PTR(bad_variables);
+        }
+
+        *ret = TAKE_PTR(n);
+        return 0;
 }
 #endif /* NM_IGNORED */
 
@@ -807,7 +968,7 @@ int getenv_bool(const char *p) {
         return parse_boolean(e);
 }
 
-int getenv_bool_secure(const char *p) {
+int secure_getenv_bool(const char *p) {
         const char *e;
 
         e = secure_getenv(p);
@@ -817,8 +978,7 @@ int getenv_bool_secure(const char *p) {
         return parse_boolean(e);
 }
 
-#if 0 /* NM_IGNORED */
-int getenv_uint64_secure(const char *p, uint64_t *ret) {
+int secure_getenv_uint64(const char *p, uint64_t *ret) {
         const char *e;
 
         assert(p);
@@ -852,9 +1012,10 @@ int putenv_dup(const char *assignment, bool override) {
         return RET_NERRNO(setenv(n, e + 1, override));
 }
 
+#if 0 /* NM_IGNORED */
 int setenv_systemd_exec_pid(bool update_only) {
-        char str[DECIMAL_STR_MAX(pid_t)];
         const char *e;
+        int r;
 
         /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
 
@@ -865,14 +1026,24 @@ int setenv_systemd_exec_pid(bool update_only) {
         if (streq_ptr(e, "*"))
                 return 0;
 
-        xsprintf(str, PID_FMT, getpid_cached());
-
-        if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
-                return -errno;
+        r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
+        if (r < 0)
+                return r;
 
         return 1;
 }
 
+int setenv_systemd_log_level(void) {
+        _cleanup_free_ char *val = NULL;
+        int r;
+
+        r = log_level_to_string_alloc(log_get_max_level(), &val);
+        if (r < 0)
+                return r;
+
+        return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
+}
+
 int getenv_path_list(const char *name, char ***ret_paths) {
         _cleanup_strv_free_ char **l = NULL;
         const char *e;
@@ -944,4 +1115,45 @@ int getenv_steal_erase(const char *name, char **ret) {
 
         return 1;
 }
+
+int set_full_environment(char **env) {
+        int r;
+
+        clearenv();
+
+        STRV_FOREACH(e, env) {
+                _cleanup_free_ char *k = NULL, *v = NULL;
+
+                r = split_pair(*e, "=", &k, &v);
+                if (r < 0)
+                        return r;
+
+                if (setenv(k, v, /* overwrite= */ true) < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
+        _cleanup_free_ char *value = NULL;
+        va_list ap;
+        int r;
+
+        assert(name);
+
+        if (!valuef)
+                return RET_NERRNO(unsetenv(name));
+
+        va_start(ap, valuef);
+        DISABLE_WARNING_FORMAT_NONLITERAL;
+        r = vasprintf(&value, valuef, ap);
+        REENABLE_WARNING;
+        va_end(ap);
+
+        if (r < 0)
+                return -ENOMEM;
+
+        return RET_NERRNO(setenv(name, value, overwrite));
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h
index b0ff5a11..6610ca8c 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.h
+++ b/src/libnm-systemd-shared/src/basic/env-util.h
@@ -19,19 +19,19 @@ bool env_name_is_valid(const char *e);
 bool env_value_is_valid(const char *e);
 bool env_assignment_is_valid(const char *e);
 
-enum {
+typedef enum ReplaceEnvFlags {
         REPLACE_ENV_USE_ENVIRONMENT = 1 << 0,
         REPLACE_ENV_ALLOW_BRACELESS = 1 << 1,
         REPLACE_ENV_ALLOW_EXTENDED  = 1 << 2,
-};
+} ReplaceEnvFlags;
 
-char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
-char **replace_env_argv(char **argv, char **env);
-
-static inline char *replace_env(const char *format, char **env, unsigned flags) {
-        return replace_env_n(format, strlen(format), env, flags);
+int replace_env_full(const char *format, size_t n, char **env, ReplaceEnvFlags flags, char **ret, char ***ret_unset_variables, char ***ret_bad_variables);
+static inline int replace_env(const char *format, char **env, ReplaceEnvFlags flags, char **ret) {
+        return replace_env_full(format, SIZE_MAX, env, flags, ret, NULL, NULL);
 }
 
+int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_variables, char ***ret_bad_variables);
+
 bool strv_env_is_valid(char **e);
 #define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
 char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@@ -43,23 +43,28 @@ char** _strv_env_merge(char **first, ...);
 #define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
 char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
 
-char **strv_env_unset(char **l, const char *p); /* In place ... */
-char **strv_env_unset_many(char **l, ...) _sentinel_;
+char** strv_env_unset(char **l, const char *p); /* In place ... */
+char** strv_env_unset_many_internal(char **l, ...) _sentinel_;
+#define strv_env_unset_many(l, ...) strv_env_unset_many_internal(l, __VA_ARGS__, NULL)
 int strv_env_replace_consume(char ***l, char *p); /* In place ... */
 int strv_env_replace_strdup(char ***l, const char *assignment);
 int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
 int strv_env_assign(char ***l, const char *key, const char *value);
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
 int _strv_env_assign_many(char ***l, ...) _sentinel_;
 #define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
 
-char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
-char *strv_env_get(char **x, const char *n) _pure_;
+char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags);
+static inline char* strv_env_get(char * const *x, const char *n) {
+        return strv_env_get_n(x, n, SIZE_MAX, 0);
+}
+
 char *strv_env_pairs_get(char **l, const char *name) _pure_;
 
 int getenv_bool(const char *p);
-int getenv_bool_secure(const char *p);
+int secure_getenv_bool(const char *p);
 
-int getenv_uint64_secure(const char *p, uint64_t *ret);
+int secure_getenv_uint64(const char *p, uint64_t *ret);
 
 /* Like setenv, but calls unsetenv if value == NULL. */
 int set_unset_env(const char *name, const char *value, bool overwrite);
@@ -68,9 +73,14 @@ int set_unset_env(const char *name, const char *value, bool overwrite);
 int putenv_dup(const char *assignment, bool override);
 
 int setenv_systemd_exec_pid(bool update_only);
+int setenv_systemd_log_level(void);
 
 /* Parses and does sanity checks on an environment variable containing
  * PATH-like colon-separated absolute paths */
 int getenv_path_list(const char *name, char ***ret_paths);
 
 int getenv_steal_erase(const char *name, char **ret);
+
+int set_full_environment(char **env);
+
+int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h
index 091f99c5..27804e63 100644
--- a/src/libnm-systemd-shared/src/basic/errno-util.h
+++ b/src/libnm-systemd-shared/src/basic/errno-util.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -73,6 +74,16 @@ static inline int RET_NERRNO(int ret) {
         return ret;
 }
 
+/* Collect possible errors in <acc>, so that the first error can be returned.
+ * Returns (possibly updated) <acc>. */
+#define RET_GATHER(acc, err)                    \
+        ({                                      \
+                int *__a = &(acc), __e = (err); \
+                if (*__a >= 0 && __e < 0)       \
+                        *__a = __e;             \
+                *__a;                           \
+        })
+
 static inline int errno_or_else(int fallback) {
         /* To be used when invoking library calls where errno handling is not defined clearly: we return
          * errno if it is set, and the specified error otherwise. The idea is that the caller initializes
@@ -84,12 +95,23 @@ static inline int errno_or_else(int fallback) {
         return -abs(fallback);
 }
 
+/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
+#define _DEFINE_ABS_WRAPPER(name)                         \
+        static inline bool ERRNO_IS_##name(intmax_t r) {  \
+                if (r == INTMAX_MIN)                      \
+                        return false;                     \
+                return ERRNO_IS_NEG_##name(-imaxabs(r));  \
+        }
+
+assert_cc(INT_MAX <= INTMAX_MAX);
+
 /* For send()/recv() or read()/write(). */
-static inline bool ERRNO_IS_TRANSIENT(int r) {
-        return IN_SET(abs(r),
-                      EAGAIN,
-                      EINTR);
+static inline bool ERRNO_IS_NEG_TRANSIENT(intmax_t r) {
+        return IN_SET(r,
+                      -EAGAIN,
+                      -EINTR);
 }
+_DEFINE_ABS_WRAPPER(TRANSIENT);
 
 /* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
  *
@@ -98,79 +120,87 @@ static inline bool ERRNO_IS_TRANSIENT(int r) {
  *
  * Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
  *          kernel tells us that with ETIMEDOUT, see tcp(7). */
-static inline bool ERRNO_IS_DISCONNECT(int r) {
-        return IN_SET(abs(r),
-                      ECONNABORTED,
-                      ECONNREFUSED,
-                      ECONNRESET,
-                      EHOSTDOWN,
-                      EHOSTUNREACH,
-                      ENETDOWN,
-                      ENETRESET,
-                      ENETUNREACH,
-                      ENONET,
-                      ENOPROTOOPT,
-                      ENOTCONN,
-                      EPIPE,
-                      EPROTO,
-                      ESHUTDOWN,
-                      ETIMEDOUT);
+static inline bool ERRNO_IS_NEG_DISCONNECT(intmax_t r) {
+        return IN_SET(r,
+                      -ECONNABORTED,
+                      -ECONNREFUSED,
+                      -ECONNRESET,
+                      -EHOSTDOWN,
+                      -EHOSTUNREACH,
+                      -ENETDOWN,
+                      -ENETRESET,
+                      -ENETUNREACH,
+                      -ENONET,
+                      -ENOPROTOOPT,
+                      -ENOTCONN,
+                      -EPIPE,
+                      -EPROTO,
+                      -ESHUTDOWN,
+                      -ETIMEDOUT);
 }
+_DEFINE_ABS_WRAPPER(DISCONNECT);
 
 /* Transient errors we might get on accept() that we should ignore. As per error handling comment in
  * the accept(2) man page. */
-static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
-        return ERRNO_IS_DISCONNECT(r) ||
-                ERRNO_IS_TRANSIENT(r) ||
-                abs(r) == EOPNOTSUPP;
+static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(intmax_t r) {
+        return ERRNO_IS_NEG_DISCONNECT(r) ||
+                ERRNO_IS_NEG_TRANSIENT(r) ||
+                r == -EOPNOTSUPP;
 }
+_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
 
 /* Resource exhaustion, could be our fault or general system trouble */
-static inline bool ERRNO_IS_RESOURCE(int r) {
-        return IN_SET(abs(r),
-                      EMFILE,
-                      ENFILE,
-                      ENOMEM);
+static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
+        return IN_SET(r,
+                      -EMFILE,
+                      -ENFILE,
+                      -ENOMEM);
 }
+_DEFINE_ABS_WRAPPER(RESOURCE);
 
 /* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
-static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
-        return IN_SET(abs(r),
-                      EOPNOTSUPP,
-                      ENOTTY,
-                      ENOSYS,
-                      EAFNOSUPPORT,
-                      EPFNOSUPPORT,
-                      EPROTONOSUPPORT,
-                      ESOCKTNOSUPPORT);
+static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
+        return IN_SET(r,
+                      -EOPNOTSUPP,
+                      -ENOTTY,
+                      -ENOSYS,
+                      -EAFNOSUPPORT,
+                      -EPFNOSUPPORT,
+                      -EPROTONOSUPPORT,
+                      -ESOCKTNOSUPPORT);
 }
+_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
 
 /* Two different errors for access problems */
-static inline bool ERRNO_IS_PRIVILEGE(int r) {
-        return IN_SET(abs(r),
-                      EACCES,
-                      EPERM);
+static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
+        return IN_SET(r,
+                      -EACCES,
+                      -EPERM);
 }
+_DEFINE_ABS_WRAPPER(PRIVILEGE);
 
 /* Three different errors for "not enough disk space" */
-static inline bool ERRNO_IS_DISK_SPACE(int r) {
-        return IN_SET(abs(r),
-                      ENOSPC,
-                      EDQUOT,
-                      EFBIG);
+static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
+        return IN_SET(r,
+                      -ENOSPC,
+                      -EDQUOT,
+                      -EFBIG);
 }
+_DEFINE_ABS_WRAPPER(DISK_SPACE);
 
 /* Three different errors for "this device does not quite exist" */
-static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
-        return IN_SET(abs(r),
-                      ENODEV,
-                      ENXIO,
-                      ENOENT);
+static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
+        return IN_SET(r,
+                      -ENODEV,
+                      -ENXIO,
+                      -ENOENT);
 }
+_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
 
 /* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
  * where it simply doesn't have the requested xattr the same way */
-static inline bool ERRNO_IS_XATTR_ABSENT(int r) {
-        return abs(r) == ENODATA ||
-                ERRNO_IS_NOT_SUPPORTED(r);
+static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
+        return r == -ENODATA ||
+                ERRNO_IS_NEG_NOT_SUPPORTED(r);
 }
+_DEFINE_ABS_WRAPPER(XATTR_ABSENT);
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index 6d2c1d4d..29f8b9cd 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -184,7 +184,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
         }
 
         case 'u': {
-                /* C++11 style 16bit unicode */
+                /* C++11 style 16-bit unicode */
 
                 int a[4];
                 size_t i;
@@ -211,7 +211,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
         }
 
         case 'U': {
-                /* C++11 style 32bit unicode */
+                /* C++11 style 32-bit unicode */
 
                 int a[8];
                 size_t i;
@@ -474,6 +474,33 @@ char* octescape(const char *s, size_t len) {
         return buf;
 }
 
+char* decescape(const char *s, const char *bad, size_t len) {
+        char *buf, *t;
+
+        /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
+
+        assert(s || len == 0);
+
+        t = buf = new(char, len * 4 + 1);
+        if (!buf)
+                return NULL;
+
+        for (size_t i = 0; i < len; i++) {
+                uint8_t u = (uint8_t) s[i];
+
+                if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
+                        *(t++) = '\\';
+                        *(t++) = '0' + (u / 100);
+                        *(t++) = '0' + ((u / 10) % 10);
+                        *(t++) = '0' + (u % 10);
+                } else
+                        *(t++) = u;
+        }
+
+        *t = 0;
+        return buf;
+}
+
 static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
         assert(bad);
         assert(t);
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 318da6f2..65caf0db 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) {
         return xescape_full(s, bad, SIZE_MAX, 0);
 }
 char* octescape(const char *s, size_t len);
+char* decescape(const char *s, const char *bad, size_t len);
 char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
 
 char* shell_escape(const char *s, const char *bad);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index 7984ddf4..1eb2e700 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -61,8 +61,8 @@ void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
         assert(p);
         assert(state);
 
-        siphash24_compress(&p->length, sizeof(p->length), state);
-        siphash24_compress(p->bytes, p->length, state);
+        siphash24_compress_typesafe(p->length, state);
+        siphash24_compress_safe(p->bytes, p->length, state);
 }
 
 DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
@@ -108,7 +108,7 @@ int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
 }
 
 static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(struct ether_addr), state);
+        siphash24_compress_typesafe(*p, state);
 }
 
 DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
@@ -272,3 +272,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) {
         *ret = a.ether;
         return 0;
 }
+
+void ether_addr_mark_random(struct ether_addr *addr) {
+        assert(addr);
+
+        /* see eth_random_addr in the kernel */
+        addr->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
+        addr->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
+}
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index 83ed77d6..187e4ef5 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) {
 
 extern const struct hash_ops ether_addr_hash_ops;
 extern const struct hash_ops ether_addr_hash_ops_free;
+
+void ether_addr_mark_random(struct ether_addr *addr);
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c
index 6781fb55..bf7bc44f 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.c
+++ b/src/libnm-systemd-shared/src/basic/extract-word.c
@@ -247,56 +247,43 @@ int extract_first_word_and_warn(
  * Let's make sure that ExtractFlags fits into an unsigned int. */
 assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned));
 
-int extract_many_words(const char **p, const char *separators, unsigned flags, ...) {
+int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) {
         va_list ap;
-        char **l;
-        int n = 0, i, c, r;
+        unsigned n = 0;
+        int r;
 
-        /* Parses a number of words from a string, stripping any
-         * quotes if necessary. */
+        /* Parses a number of words from a string, stripping any quotes if necessary. */
 
         assert(p);
 
         /* Count how many words are expected */
         va_start(ap, flags);
-        for (;;) {
-                if (!va_arg(ap, char **))
-                        break;
+        while (va_arg(ap, char**))
                 n++;
-        }
         va_end(ap);
 
-        if (n <= 0)
+        if (n == 0)
                 return 0;
 
         /* Read all words into a temporary array */
-        l = newa0(char*, n);
-        for (c = 0; c < n; c++) {
+        char **l = newa0(char*, n);
+        unsigned c;
 
+        for (c = 0; c < n; c++) {
                 r = extract_first_word(p, &l[c], separators, flags);
                 if (r < 0) {
-                        int j;
-
-                        for (j = 0; j < c; j++)
-                                free(l[j]);
-
+                        free_many_charp(l, c);
                         return r;
                 }
-
                 if (r == 0)
                         break;
         }
 
-        /* If we managed to parse all words, return them in the passed
-         * in parameters */
+        /* If we managed to parse all words, return them in the passed in parameters */
         va_start(ap, flags);
-        for (i = 0; i < n; i++) {
-                char **v;
-
-                v = va_arg(ap, char **);
-                assert(v);
-
-                *v = l[i];
+        FOREACH_ARRAY(i, l, n) {
+                char **v = ASSERT_PTR(va_arg(ap, char**));
+                *v = *i;
         }
         va_end(ap);
 
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.h b/src/libnm-systemd-shared/src/basic/extract-word.h
index c82ad761..da4f6ae6 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.h
+++ b/src/libnm-systemd-shared/src/basic/extract-word.h
@@ -19,4 +19,7 @@ typedef enum ExtractFlags {
 
 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
 int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
-int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_;
+
+int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) _sentinel_;
+#define extract_many_words(p, separators, flags, ...) \
+        extract_many_words_internal(p, separators, flags, ##__VA_ARGS__, NULL)
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index a0e2f4eb..584d02f0 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -94,11 +94,25 @@ void safe_close_pair(int p[static 2]) {
         p[1] = safe_close(p[1]);
 }
 
-void close_many(const int fds[], size_t n_fd) {
-        assert(fds || n_fd <= 0);
+void close_many(const int fds[], size_t n_fds) {
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fd; i++)
-                safe_close(fds[i]);
+        FOREACH_ARRAY(fd, fds, n_fds)
+                safe_close(*fd);
+}
+
+void close_many_unset(int fds[], size_t n_fds) {
+        assert(fds || n_fds == 0);
+
+        FOREACH_ARRAY(fd, fds, n_fds)
+                *fd = safe_close(*fd);
+}
+
+void close_many_and_free(int *fds, size_t n_fds) {
+        assert(fds || n_fds == 0);
+
+        close_many(fds, n_fds);
+        free(fds);
 }
 
 int fclose_nointr(FILE *f) {
@@ -158,6 +172,19 @@ int fd_nonblock(int fd, bool nonblock) {
         return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
 }
 
+int stdio_disable_nonblock(void) {
+        int ret = 0;
+
+        /* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as
+         * write()s might unexpectedly fail with EAGAIN. */
+
+        RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false));
+        RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false));
+        RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false));
+
+        return ret;
+}
+
 int fd_cloexec(int fd, bool cloexec) {
         int flags, nflags;
 
@@ -176,32 +203,32 @@ int fd_cloexec(int fd, bool cloexec) {
 
 #if 0 /* NM_IGNORED */
 int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
-        int ret = 0, r;
+        int r = 0;
 
-        assert(n_fds == 0 || fds);
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fds; i++) {
-                if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
+        FOREACH_ARRAY(fd, fds, n_fds) {
+                if (*fd < 0) /* Skip gracefully over already invalidated fds */
                         continue;
 
-                r = fd_cloexec(fds[i], cloexec);
-                if (r < 0 && ret >= 0) /* Continue going, but return first error */
-                        ret = r;
-                else
-                        ret = 1; /* report if we did anything */
+                RET_GATHER(r, fd_cloexec(*fd, cloexec));
+
+                if (r >= 0)
+                        r = 1; /* report if we did anything */
         }
 
-        return ret;
+        return r;
 }
 
-_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
-        assert(n_fdset == 0 || fdset);
+static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
+        assert(fd >= 0);
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fdset; i++) {
-                if (fdset[i] < 0)
+        FOREACH_ARRAY(i, fds, n_fds) {
+                if (*i < 0)
                         continue;
 
-                if (fdset[i] == fd)
+                if (*i == fd)
                         return true;
         }
 
@@ -232,7 +259,7 @@ int get_max_fd(void) {
 static int close_all_fds_frugal(const int except[], size_t n_except) {
         int max_fd, r = 0;
 
-        assert(n_except == 0 || except);
+        assert(except || n_except == 0);
 
         /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
          * and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
@@ -247,8 +274,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
          * spin the CPU for a long time. */
         if (max_fd > MAX_FD_LOOP_LIMIT)
                 return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
-                                       "Refusing to loop over %d potential fds.",
-                                       max_fd);
+                                       "Refusing to loop over %d potential fds.", max_fd);
 
         for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
                 int q;
@@ -257,8 +283,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
                         continue;
 
                 q = close_nointr(fd);
-                if (q < 0 && q != -EBADF && r >= 0)
-                        r = q;
+                if (q != -EBADF)
+                        RET_GATHER(r, q);
         }
 
         return r;
@@ -285,7 +311,7 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
         case 0:
                 /* Close everything. Yay! */
 
-                if (close_range(3, -1, 0) >= 0)
+                if (close_range(3, INT_MAX, 0) >= 0)
                         return 1;
 
                 if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
@@ -396,7 +422,7 @@ int close_all_fds(const int except[], size_t n_except) {
                                 if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
                                         return 0;
 
-                                if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
+                                if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) >= 0)
                                         return 0;
 
                                 if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
@@ -441,6 +467,53 @@ int close_all_fds(const int except[], size_t n_except) {
         return r;
 }
 
+int pack_fds(int fds[], size_t n_fds) {
+        if (n_fds <= 0)
+                return 0;
+
+        /* Shifts around the fds in the provided array such that they
+         * all end up packed next to each-other, in order, starting
+         * from SD_LISTEN_FDS_START. This must be called after close_all_fds();
+         * it is likely to freeze up otherwise. You should probably use safe_fork_full
+         * with FORK_CLOSE_ALL_FDS|FORK_PACK_FDS set, to ensure that this is done correctly.
+         * The fds array is modified in place with the new FD numbers. */
+
+        assert(fds);
+
+        for (int start = 0;;) {
+                int restart_from = -1;
+
+                for (int i = start; i < (int) n_fds; i++) {
+                        int nfd;
+
+                        /* Already at right index? */
+                        if (fds[i] == i + 3)
+                                continue;
+
+                        nfd = fcntl(fds[i], F_DUPFD, i + 3);
+                        if (nfd < 0)
+                                return -errno;
+
+                        safe_close(fds[i]);
+                        fds[i] = nfd;
+
+                        /* Hmm, the fd we wanted isn't free? Then
+                         * let's remember that and try again from here */
+                        if (nfd != i + 3 && restart_from < 0)
+                                restart_from = i;
+                }
+
+                if (restart_from < 0)
+                        break;
+
+                start = restart_from;
+        }
+
+        assert(fds[0] == 3);
+
+        return 0;
+}
+
 int same_fd(int a, int b) {
         struct stat sta, stb;
         pid_t pid;
@@ -589,7 +662,7 @@ int move_fd(int from, int to, int cloexec) {
                 if (fl < 0)
                         return -errno;
 
-                cloexec = !!(fl & FD_CLOEXEC);
+                cloexec = FLAGS_SET(fl, FD_CLOEXEC);
         }
 
         r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
@@ -647,7 +720,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
                       original_output_fd,
                       original_error_fd },
             null_fd = -EBADF,                        /* If we open /dev/null, we store the fd to it here */
-            copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
+            copy_fd[3] = EBADF_TRIPLET,              /* This contains all fds we duplicate here
                                                       * temporarily, and hence need to close at the end. */
             r;
         bool null_readable, null_writable;
@@ -745,8 +818,7 @@ finish:
                 safe_close_above_stdio(original_error_fd);
 
         /* Close the copies we moved > 2 */
-        for (int i = 0; i < 3; i++)
-                safe_close(copy_fd[i]);
+        close_many(copy_fd, 3);
 
         /* Close our null fd, if it's > 2 */
         safe_close_above_stdio(null_fd);
@@ -756,9 +828,10 @@ finish:
 #endif /* NM_IGNORED */
 
 int fd_reopen(int fd, int flags) {
-        int new_fd, r;
+        int r;
 
         assert(fd >= 0 || fd == AT_FDCWD);
+        assert(!FLAGS_SET(flags, O_CREAT));
 
         /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
          * turn O_RDWR fds into O_RDONLY fds.
@@ -782,19 +855,12 @@ int fd_reopen(int fd, int flags) {
                  * the same way as the non-O_DIRECTORY case. */
                 return -ELOOP;
 
-        if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) {
+        if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD)
                 /* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
                  * magic /proc/ directory, and make ourselves independent of that being mounted. */
-                new_fd = openat(fd, ".", flags | O_DIRECTORY);
-                if (new_fd < 0)
-                        return -errno;
-
-                return new_fd;
-        }
-
-        assert(fd >= 0);
+                return RET_NERRNO(openat(fd, ".", flags | O_DIRECTORY));
 
-        new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
+        int new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
         if (new_fd < 0) {
                 if (errno != ENOENT)
                         return -errno;
@@ -811,7 +877,6 @@ int fd_reopen(int fd, int flags) {
         return new_fd;
 }
 
-#if 0 /* NM_IGNORED */
 int fd_reopen_condition(
                 int fd,
                 int flags,
@@ -821,6 +886,7 @@ int fd_reopen_condition(
         int r, new_fd;
 
         assert(fd >= 0);
+        assert(!FLAGS_SET(flags, O_CREAT));
 
         /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
          * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
@@ -843,6 +909,7 @@ int fd_reopen_condition(
         return new_fd;
 }
 
+#if 0 /* NM_IGNORED */
 int fd_is_opath(int fd) {
         int r;
 
@@ -855,6 +922,38 @@ int fd_is_opath(int fd) {
         return FLAGS_SET(r, O_PATH);
 }
 
+int fd_verify_safe_flags(int fd) {
+        int flags, unexpected_flags;
+
+        /* Check if an extrinsic fd is safe to work on (by a privileged service). This ensures that clients
+         * can't trick a privileged service into giving access to a file the client doesn't already have
+         * access to (especially via something like O_PATH).
+         *
+         * O_NOFOLLOW: For some reason the kernel will return this flag from fcntl; it doesn't go away
+         *             immediately after open(). It should have no effect whatsoever to an already-opened FD,
+         *             and since we refuse O_PATH it should be safe.
+         *
+         * RAW_O_LARGEFILE: glibc secretly sets this and neglects to hide it from us if we call fcntl.
+         *                  See comment in missing_fcntl.h for more details about this.
+         *
+         * O_DIRECTORY: this is set for directories, which are totally fine
+         */
+
+        assert(fd >= 0);
+
+        flags = fcntl(fd, F_GETFL);
+        if (flags < 0)
+                return -errno;
+
+        unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|O_DIRECTORY);
+        if (unexpected_flags != 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
+                                       "Unexpected flags set for extrinsic fd: 0%o",
+                                       (unsigned) unexpected_flags);
+
+        return 0;
+}
+
 int read_nr_open(void) {
         _cleanup_free_ char *nr_open = NULL;
         int r;
@@ -901,75 +1000,86 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
 }
 
 int path_is_root_at(int dir_fd, const char *path) {
-        STRUCT_NEW_STATX_DEFINE(st);
-        STRUCT_NEW_STATX_DEFINE(pst);
-        _cleanup_close_ int fd = -EBADF;
-        int r;
+        _cleanup_close_ int fd = -EBADF, pfd = -EBADF;
 
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
 
         if (!isempty(path)) {
-                fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+                fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
                 if (fd < 0)
-                        return -errno;
+                        return errno == ENOTDIR ? false : -errno;
 
                 dir_fd = fd;
         }
 
-        r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
-        if (r == -ENOTDIR)
-                return false;
+        pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC);
+        if (pfd < 0)
+                return errno == ENOTDIR ? false : -errno;
+
+        /* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
+         * and we also need to check that the mount ids are the same. Otherwise, a construct like the
+         * following could be used to trick us:
+         *
+         * $ mkdir /tmp/x /tmp/x/y
+         * $ mount --bind /tmp/x /tmp/x/y
+         */
+
+        return fds_are_same_mount(dir_fd, pfd);
+}
+
+int fds_are_same_mount(int fd1, int fd2) {
+        STRUCT_NEW_STATX_DEFINE(st1);
+        STRUCT_NEW_STATX_DEFINE(st2);
+        int r;
+
+        assert(fd1 >= 0);
+        assert(fd2 >= 0);
+
+        r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
         if (r < 0)
                 return r;
 
-        r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx);
+        r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
         if (r < 0)
                 return r;
 
         /* First, compare inode. If these are different, the fd does not point to the root directory "/". */
-        if (!statx_inode_same(&st.sx, &pst.sx))
+        if (!statx_inode_same(&st1.sx, &st2.sx))
                 return false;
 
-        /* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
-         * and we also need to check that the mount ids are the same. Otherwise, a construct like the
-         * following could be used to trick us:
-         *
-         * $ mkdir /tmp/x /tmp/x/y
-         * $ mount --bind /tmp/x /tmp/x/y
-         *
-         * Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
-         * kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
-         * mount points in an early boot stage, and silently skip the following check. */
+        /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
+         * kernel is used. In that case, let's assume that we do not have such spurious mount points in an
+         * early boot stage, and silently skip the following check. */
 
-        if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
+        if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
                 int mntid;
 
-                r = path_get_mnt_id_at(dir_fd, "", &mntid);
-                if (r == -ENOSYS)
+                r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
+                if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
                         return true; /* skip the mount ID check */
                 if (r < 0)
                         return r;
                 assert(mntid >= 0);
 
-                st.nsx.stx_mnt_id = mntid;
-                st.nsx.stx_mask |= STATX_MNT_ID;
+                st1.nsx.stx_mnt_id = mntid;
+                st1.nsx.stx_mask |= STATX_MNT_ID;
         }
 
-        if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
+        if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
                 int mntid;
 
-                r = path_get_mnt_id_at(dir_fd, "..", &mntid);
-                if (r == -ENOSYS)
+                r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
+                if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
                         return true; /* skip the mount ID check */
                 if (r < 0)
                         return r;
                 assert(mntid >= 0);
 
-                pst.nsx.stx_mnt_id = mntid;
-                pst.nsx.stx_mask |= STATX_MNT_ID;
+                st2.nsx.stx_mnt_id = mntid;
+                st2.nsx.stx_mask |= STATX_MNT_ID;
         }
 
-        return statx_mount_same(&st.nsx, &pst.nsx);
+        return statx_mount_same(&st1.nsx, &st2.nsx);
 }
 
 const char *accmode_to_string(int flags) {
@@ -984,4 +1094,12 @@ const char *accmode_to_string(int flags) {
                 return NULL;
         }
 }
+
+char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) {
+        assert(buf);
+        assert(fd >= 0);
+        assert(pid >= 0);
+        assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd));
+        return buf;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index c870a1b8..f5498310 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -8,6 +8,7 @@
 #include <sys/socket.h>
 
 #include "macro.h"
+#include "missing_fcntl.h"
 #include "stdio-util.h"
 
 /* maximum length of fdname */
@@ -16,7 +17,10 @@
 /* Make sure we can distinguish fd 0 and NULL */
 #define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
 #define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
-#define PIPE_EBADF { -EBADF, -EBADF }
+
+/* Useful helpers for initializing pipe(), socketpair() or stdio fd arrays */
+#define EBADF_PAIR { -EBADF, -EBADF }
+#define EBADF_TRIPLET { -EBADF, -EBADF, -EBADF }
 
 int close_nointr(int fd);
 int safe_close(int fd);
@@ -29,7 +33,9 @@ static inline int safe_close_above_stdio(int fd) {
         return safe_close(fd);
 }
 
-void close_many(const int fds[], size_t n_fd);
+void close_many(const int fds[], size_t n_fds);
+void close_many_unset(int fds[], size_t n_fds);
+void close_many_and_free(int *fds, size_t n_fds);
 
 int fclose_nointr(FILE *f);
 FILE* safe_fclose(FILE *f);
@@ -47,6 +53,11 @@ static inline void fclosep(FILE **f) {
         safe_fclose(*f);
 }
 
+static inline void* close_fd_ptr(void *p) {
+        safe_close(PTR_TO_FD(p));
+        return NULL;
+}
+
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
 
@@ -57,6 +68,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
 #define _cleanup_close_pair_ _cleanup_(close_pairp)
 
 int fd_nonblock(int fd, bool nonblock);
+int stdio_disable_nonblock(void);
+
 int fd_cloexec(int fd, bool cloexec);
 int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
 
@@ -65,6 +78,8 @@ int get_max_fd(void);
 int close_all_fds(const int except[], size_t n_except);
 int close_all_fds_without_malloc(const int except[], size_t n_except);
 
+int pack_fds(int fds[], size_t n);
+
 int same_fd(int a, int b);
 
 void cmsg_close_all(struct msghdr *mh);
@@ -97,11 +112,17 @@ static inline int make_null_stdio(void) {
 
 int fd_reopen(int fd, int flags);
 int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
+
 int fd_is_opath(int fd);
+int fd_verify_safe_flags(int fd);
+
 int read_nr_open(void);
 int fd_get_diskseq(int fd, uint64_t *ret);
 
 int path_is_root_at(int dir_fd, const char *path);
+static inline int path_is_root(const char *path) {
+        return path_is_root_at(AT_FDCWD, path);
+}
 static inline int dir_fd_is_root(int dir_fd) {
         return path_is_root_at(dir_fd, NULL);
 }
@@ -109,6 +130,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) {
         return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
 }
 
+int fds_are_same_mount(int fd1, int fd2);
+
 /* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
 #define PROC_FD_PATH_MAX \
         (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
@@ -123,6 +146,16 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f
 #define FORMAT_PROC_FD_PATH(fd) \
         format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
 
+/* The maximum length a buffer for a /proc/<pid>/fd/<fd> path needs */
+#define PROC_PID_FD_PATH_MAX \
+        (STRLEN("/proc//fd/") + DECIMAL_STR_MAX(pid_t) + DECIMAL_STR_MAX(int))
+
+char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd);
+
+/* Kinda the same as FORMAT_PROC_FD_PATH(), but goes by PID rather than "self" symlink */
+#define FORMAT_PROC_PID_FD_PATH(pid, fd)                                \
+        format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd))
+
 const char *accmode_to_string(int flags);
 
 /* Like ASSERT_PTR, but for fds */
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 908a0309..7ab29816 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -30,10 +30,13 @@
 #include "stdio-util.h"
 #include "string-util.h"
 #include "sync-util.h"
+#include "terminal-util.h"
 #include "tmpfile-util.h"
 
 /* The maximum size of the file we'll read in one go in read_full_file() (64M). */
 #define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
+/* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */
+#define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3
 
 /* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
  * in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
@@ -200,6 +203,19 @@ int write_string_stream_ts(
         return 0;
 }
 
+static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
+
+        /* We support three different modes, that are the ones that really make sense for text files like this:
+         *
+         *     → 0600 (i.e. root-only)
+         *     → 0444 (i.e. read-only)
+         *     → 0644 (i.e. writable for root, readable for everyone else)
+         */
+
+        return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
+                FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
+}
+
 static int write_string_file_atomic_at(
                 int dir_fd,
                 const char *fn,
@@ -225,7 +241,7 @@ static int write_string_file_atomic_at(
         if (r < 0)
                 goto fail;
 
-        r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
+        r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
         if (r < 0)
                 goto fail;
 
@@ -288,7 +304,7 @@ int write_string_file_ts_at(
                     (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
                     (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
                     (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
-                    (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
+                    write_string_file_flags_to_mode(flags));
         if (fd < 0) {
                 r = -errno;
                 goto fail;
@@ -576,7 +592,7 @@ int read_full_stream_full(
                 size_t *ret_size) {
 
         _cleanup_free_ char *buf = NULL;
-        size_t n, n_next = 0, l;
+        size_t n, n_next = 0, l, expected_decoded_size = size;
         int fd, r;
 
         assert(f);
@@ -587,6 +603,13 @@ int read_full_stream_full(
         if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
                 return -ERANGE;
 
+        if ((flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) != 0) {
+                if (size <= SIZE_MAX / READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY)
+                        size *= READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY;
+                else
+                        size = SIZE_MAX;
+        }
+
         fd = fileno(f);
         if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
                         * fmemopen()), let's optimize our buffering */
@@ -711,6 +734,11 @@ int read_full_stream_full(
                         explicit_bzero_safe(buf, n);
                 free_and_replace(buf, decoded);
                 n = l = decoded_size;
+
+                if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > expected_decoded_size) {
+                        r = -E2BIG;
+                        goto finalize;
+                }
         }
 
         if (!ret_size) {
@@ -1057,7 +1085,9 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
         if (mode_flags < 0)
                 return mode_flags;
 
-        copy_fd = fd_reopen(fd, mode_flags);
+        /* Flags returned by fopen_mode_to_flags might contain O_CREAT, but it doesn't make sense for fd_reopen
+         * since we're working on an existing fd anyway. Let's drop it here to avoid triggering assertion. */
+        copy_fd = fd_reopen(fd, mode_flags & ~O_CREAT);
         if (copy_fd < 0)
                 return copy_fd;
 
@@ -1069,123 +1099,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
         return 0;
 }
 
-static int search_and_fopen_internal(
+static int search_and_open_internal(
                 const char *path,
-                const char *mode,
+                int mode,            /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
                 const char *root,
                 char **search,
-                FILE **ret,
+                int *ret_fd,
                 char **ret_path) {
 
+        int r;
+
+        assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
         assert(path);
-        assert(mode);
-        assert(ret);
+
+        if (path_is_absolute(path)) {
+                _cleanup_close_ int fd = -EBADF;
+
+                if (ret_fd)
+                        /* We only specify 0777 here to appease static analyzers, it's never used since we
+                         * don't support O_CREAT here */
+                        r = fd = RET_NERRNO(open(path, mode, 0777));
+                else
+                        r = RET_NERRNO(access(path, mode));
+                if (r < 0)
+                        return r;
+
+                if (ret_path) {
+                        r = path_simplify_alloc(path, ret_path);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (ret_fd)
+                        *ret_fd = TAKE_FD(fd);
+
+                return 0;
+        }
 
         if (!path_strv_resolve_uniq(search, root))
                 return -ENOMEM;
 
         STRV_FOREACH(i, search) {
+                _cleanup_close_ int fd = -EBADF;
                 _cleanup_free_ char *p = NULL;
-                FILE *f;
 
                 p = path_join(root, *i, path);
                 if (!p)
                         return -ENOMEM;
 
-                f = fopen(p, mode);
-                if (f) {
+                if (ret_fd)
+                        /* as above, 0777 is static analyzer appeasement */
+                        r = fd = RET_NERRNO(open(p, mode, 0777));
+                else
+                        r = RET_NERRNO(access(p, F_OK));
+                if (r >= 0) {
                         if (ret_path)
                                 *ret_path = path_simplify(TAKE_PTR(p));
 
-                        *ret = f;
+                        if (ret_fd)
+                                *ret_fd = TAKE_FD(fd);
+
                         return 0;
                 }
-
-                if (errno != ENOENT)
-                        return -errno;
+                if (r != -ENOENT)
+                        return r;
         }
 
         return -ENOENT;
 }
 
-int search_and_fopen(
-                const char *filename,
-                const char *mode,
+int search_and_open(
+                const char *path,
+                int mode,
                 const char *root,
-                const char **search,
-                FILE **ret,
+                char **search,
+                int *ret_fd,
                 char **ret_path) {
 
         _cleanup_strv_free_ char **copy = NULL;
 
-        assert(filename);
-        assert(mode);
-        assert(ret);
+        assert(path);
 
-        if (path_is_absolute(filename)) {
-                _cleanup_fclose_ FILE *f = NULL;
+        copy = strv_copy((char**) search);
+        if (!copy)
+                return -ENOMEM;
+
+        return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
+}
+
+static int search_and_fopen_internal(
+                const char *path,
+                const char *mode,
+                const char *root,
+                char **search,
+                FILE **ret_file,
+                char **ret_path) {
+
+        _cleanup_free_ char *found_path = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        assert(path);
+        assert(mode || !ret_file);
+
+        r = search_and_open(
+                        path,
+                        mode ? fopen_mode_to_flags(mode) : 0,
+                        root,
+                        search,
+                        ret_file ? &fd : NULL,
+                        ret_path ? &found_path : NULL);
+        if (r < 0)
+                return r;
 
-                f = fopen(filename, mode);
+        if (ret_file) {
+                FILE *f = take_fdopen(&fd, mode);
                 if (!f)
                         return -errno;
 
-                if (ret_path) {
-                        char *p;
+                *ret_file = f;
+        }
 
-                        p = strdup(filename);
-                        if (!p)
-                                return -ENOMEM;
+        if (ret_path)
+                *ret_path = TAKE_PTR(found_path);
 
-                        *ret_path = path_simplify(p);
-                }
+        return 0;
+}
 
-                *ret = TAKE_PTR(f);
-                return 0;
-        }
+int search_and_fopen(
+                const char *path,
+                const char *mode,
+                const char *root,
+                const char **search,
+                FILE **ret_file,
+                char **ret_path) {
+
+        _cleanup_strv_free_ char **copy = NULL;
+
+        assert(path);
+        assert(mode || !ret_file);
 
         copy = strv_copy((char**) search);
         if (!copy)
                 return -ENOMEM;
 
-        return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
+        return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
 }
 
 int search_and_fopen_nulstr(
-                const char *filename,
+                const char *path,
                 const char *mode,
                 const char *root,
                 const char *search,
-                FILE **ret,
+                FILE **ret_file,
                 char **ret_path) {
 
-        _cleanup_strv_free_ char **s = NULL;
-
-        if (path_is_absolute(filename)) {
-                _cleanup_fclose_ FILE *f = NULL;
-
-                f = fopen(filename, mode);
-                if (!f)
-                        return -errno;
-
-                if (ret_path) {
-                        char *p;
-
-                        p = strdup(filename);
-                        if (!p)
-                                return -ENOMEM;
-
-                        *ret_path = path_simplify(p);
-                }
+        _cleanup_strv_free_ char **l = NULL;
 
-                *ret = TAKE_PTR(f);
-                return 0;
-        }
+        assert(path);
+        assert(mode || !ret_file);
 
-        s = strv_split_nulstr(search);
-        if (!s)
+        l = strv_split_nulstr(search);
+        if (!l)
                 return -ENOMEM;
 
-        return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
+        return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
 }
 #endif /* NM_IGNORED */
 
@@ -1259,33 +1337,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
 }
 #endif /* NM_IGNORED */
 
-int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
-        int r;
-
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) {
         assert(s);
+        assert(space);
 
-        /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
-         * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
-         * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
-         * element, but not before the first one. */
+        /* Outputs the specified string with fputs(), but optionally prefixes it with a separator.
+         * The *space parameter when specified shall initially point to a boolean variable initialized
+         * to false. It is set to true after the first invocation. This call is supposed to be use in loops,
+         * where a separator shall be inserted between each element, but not before the first one. */
 
         if (!f)
                 f = stdout;
 
-        if (space) {
-                if (!separator)
-                        separator = " ";
+        if (!separator)
+                separator = " ";
 
-                if (*space) {
-                        r = fputs(separator, f);
-                        if (r < 0)
-                                return r;
-                }
+        if (*space)
+                if (fputs(separator, f) < 0)
+                        return -EIO;
 
-                *space = true;
-        }
+        *space = true;
+
+        if (fputs(s, f) < 0)
+                return -EIO;
 
-        return fputs(s, f);
+        return 0;
 }
 
 #if 0 /* NM_IGNORED */
@@ -1406,7 +1482,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
                                                      * and don't call isatty() on an invalid fd */
                                                 flags |= READ_LINE_NOT_A_TTY;
                                         else
-                                                flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
+                                                flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
                                 }
                                 if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
                                         break;
@@ -1437,6 +1513,36 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
         return (int) count;
 }
 
+int read_stripped_line(FILE *f, size_t limit, char **ret) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        assert(f);
+
+        r = read_line(f, limit, ret ? &s : NULL);
+        if (r < 0)
+                return r;
+
+        if (ret) {
+                const char *p;
+
+                p = strstrip(s);
+                if (p == s)
+                        *ret = TAKE_PTR(s);
+                else {
+                        char *copy;
+
+                        copy = strdup(p);
+                        if (!copy)
+                                return -ENOMEM;
+
+                        *ret = copy;
+                }
+        }
+
+        return r;
+}
+
 int safe_fgetc(FILE *f, char *ret) {
         int k;
 
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index 769bf394..03c3f3ff 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -26,7 +26,8 @@ typedef enum {
         WRITE_STRING_FILE_NOFOLLOW                   = 1 << 8,
         WRITE_STRING_FILE_MKDIR_0755                 = 1 << 9,
         WRITE_STRING_FILE_MODE_0600                  = 1 << 10,
-        WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
+        WRITE_STRING_FILE_MODE_0444                  = 1 << 11,
+        WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
 
         /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
            more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
@@ -129,8 +130,12 @@ static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret)
 
 int fdopen_independent(int fd, const char *mode, FILE **ret);
 
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
+int search_and_open(const char *path, int mode, const char *root, char **search, int *ret_fd, char **ret_path);
+static inline int search_and_access(const char *path, int mode, const char *root, char**search, char **ret_path) {
+        return search_and_open(path, mode, root, search, NULL, ret_path);
+}
+int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret_file, char **ret_path);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret_file, char **ret_path);
 
 int fflush_and_check(FILE *f);
 int fflush_sync_and_check(FILE *f);
@@ -138,7 +143,7 @@ int fflush_sync_and_check(FILE *f);
 int write_timestamp_file_atomic(const char *fn, usec_t n);
 int read_timestamp_file(const char *fn, usec_t *ret);
 
-int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space);
 
 typedef enum ReadLineFlags {
         READ_LINE_ONLY_NUL  = 1 << 0,
@@ -162,6 +167,8 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
         return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
 }
 
+int read_stripped_line(FILE *f, size_t limit, char **ret);
+
 int safe_fgetc(FILE *f, char *ret);
 
 int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
diff --git a/src/libnm-systemd-shared/src/basic/format-util.h b/src/libnm-systemd-shared/src/basic/format-util.h
index 7db8b61d..8a80eb33 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.h
+++ b/src/libnm-systemd-shared/src/basic/format-util.h
@@ -18,6 +18,14 @@ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
 assert_cc(sizeof(gid_t) == sizeof(uint32_t));
 #define GID_FMT "%" PRIu32
 
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 §6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define FORMAT_UID(uid) \
+        snprintf_ok((char[DECIMAL_STR_MAX(uid_t)]){}, DECIMAL_STR_MAX(uid_t), UID_FMT, uid)
+#define FORMAT_GID(gid) \
+        snprintf_ok((char[DECIMAL_STR_MAX(gid_t)]){}, DECIMAL_STR_MAX(gid_t), GID_FMT, gid)
+
 #if SIZEOF_TIME_T == 8
 #  define PRI_TIME PRIi64
 #elif SIZEOF_TIME_T == 4
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 32fd849d..3f414794 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -11,6 +11,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "btrfs.h"
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -119,7 +120,11 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
 int readlinkat_malloc(int fd, const char *p, char **ret) {
         size_t l = PATH_MAX;
 
-        assert(p);
+        assert(fd >= 0 || fd == AT_FDCWD);
+
+        if (fd < 0 && isempty(p))
+                return -EISDIR; /* In this case, the fd points to the current working directory, and is
+                                 * definitely not a symlink. Let's return earlier. */
 
         for (;;) {
                 _cleanup_free_ char *c = NULL;
@@ -129,7 +134,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
                 if (!c)
                         return -ENOMEM;
 
-                n = readlinkat(fd, p, c, l);
+                n = readlinkat(fd, strempty(p), c, l);
                 if (n < 0)
                         return -errno;
 
@@ -155,7 +160,6 @@ int readlink_malloc(const char *p, char **ret) {
         return readlinkat_malloc(AT_FDCWD, p, ret);
 }
 
-#if 0 /* NM_IGNORED */
 int readlink_value(const char *p, char **ret) {
         _cleanup_free_ char *link = NULL, *name = NULL;
         int r;
@@ -177,6 +181,7 @@ int readlink_value(const char *p, char **ret) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 int readlink_and_make_absolute(const char *p, char **ret) {
         _cleanup_free_ char *target = NULL;
         int r;
@@ -292,8 +297,22 @@ int fchmod_umask(int fd, mode_t m) {
 
 int fchmod_opath(int fd, mode_t m) {
         /* This function operates also on fd that might have been opened with
-         * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
-         * fchownat() does. */
+         * O_PATH. The tool set we have is non-intuitive:
+         * - fchmod(2) only operates on open files (i. e., fds with an open file description);
+         * - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
+         *   + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
+         *     but does not support AT_EMPTY_PATH (only supports AT_SYMLINK_NOFOLLOW);
+         * - fchmodat2(2) supports all the AT_* flags, but is still very recent.
+         *
+         * We try to use fchmodat2(), and, if it is not supported, resort
+         * to the /proc/self/fd dance. */
+
+        assert(fd >= 0);
+
+        if (fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0)
+                return 0;
+        if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
+                return -errno;
 
         if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
                 if (errno != ENOENT)
@@ -1045,7 +1064,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
                 path = fname;
         }
 
-        fd = xopenat(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
+        fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
         if (IN_SET(fd, -ELOOP, -ENOTDIR))
                 return -EEXIST;
         if (fd < 0)
@@ -1101,13 +1120,23 @@ int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, b
         }
 }
 
-int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
+int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
         _cleanup_close_ int fd = -EBADF;
         bool made = false;
         int r;
 
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
 
+        /* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
+         *
+         *   • O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
+         *     opened. When used with the XO_SUBVOLUME flag this will even create a btrfs subvolume.
+         *
+         *   • If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
+         *
+         *   • If the path is specified NULL or empty, behaves like fd_reopen().
+         */
+
         if (isempty(path)) {
                 assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
                 return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
@@ -1120,7 +1149,10 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
         }
 
         if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
-                r = RET_NERRNO(mkdirat(dir_fd, path, mode));
+                if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
+                        r = btrfs_subvol_make_fallback(dir_fd, path, mode);
+                else
+                        r = RET_NERRNO(mkdirat(dir_fd, path, mode));
                 if (r == -EEXIST) {
                         if (FLAGS_SET(open_flags, O_EXCL))
                                 return -EEXIST;
@@ -1170,7 +1202,7 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
 }
 
 #if 0 /* NM_IGNORED */
-int xopenat_lock(
+int xopenat_lock_full(
                 int dir_fd,
                 const char *path,
                 int open_flags,
@@ -1183,18 +1215,17 @@ int xopenat_lock(
         int r;
 
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
-        assert(path);
         assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
 
         /* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
          * the same error here). */
-        if (FLAGS_SET(open_flags, O_DIRECTORY) && locktype != LOCK_BSD)
+        if (FLAGS_SET(open_flags, O_DIRECTORY) && !IN_SET(locktype, LOCK_BSD, LOCK_NONE))
                 return -EBADF;
 
         for (;;) {
                 struct stat st;
 
-                fd = xopenat(dir_fd, path, open_flags, xopen_flags, mode);
+                fd = xopenat_full(dir_fd, path, open_flags, xopen_flags, mode);
                 if (fd < 0)
                         return fd;
 
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index a19836d1..6a1e2e76 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -133,9 +133,16 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
 int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
 
 typedef enum XOpenFlags {
-        XO_LABEL = 1 << 0,
+        XO_LABEL     = 1 << 0,
+        XO_SUBVOLUME = 1 << 1,
 } XOpenFlags;
 
-int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
+int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
+static inline int xopenat(int dir_fd, const char *path, int open_flags) {
+        return xopenat_full(dir_fd, path, open_flags, 0, 0);
+}
 
-int xopenat_lock(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode, LockType locktype, int operation);
+int xopenat_lock_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode, LockType locktype, int operation);
+static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, LockType locktype, int operation) {
+        return xopenat_lock_full(dir_fd, path, open_flags, 0, 0, locktype, operation);
+}
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c
index 1ea8a645..58d64a03 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.c
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.c
@@ -25,7 +25,7 @@ bool emoji_enabled(void) {
         return cached_emoji_enabled;
 }
 
-const char *special_glyph(SpecialGlyph code) {
+const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
 
         /* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
          * conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@@ -43,6 +43,8 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_TREE_SPACE]              = "  ",
                         [SPECIAL_GLYPH_TREE_TOP]                = ",-",
                         [SPECIAL_GLYPH_VERTICAL_DOTTED]         = ":",
+                        [SPECIAL_GLYPH_HORIZONTAL_DOTTED]       = "-",
+                        [SPECIAL_GLYPH_HORIZONTAL_FAT]          = "=",
                         [SPECIAL_GLYPH_TRIANGULAR_BULLET]       = ">",
                         [SPECIAL_GLYPH_BLACK_CIRCLE]            = "*",
                         [SPECIAL_GLYPH_WHITE_CIRCLE]            = "*",
@@ -54,11 +56,12 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_CROSS_MARK]              = "-",
                         [SPECIAL_GLYPH_LIGHT_SHADE]             = "-",
                         [SPECIAL_GLYPH_DARK_SHADE]              = "X",
+                        [SPECIAL_GLYPH_FULL_BLOCK]              = "#",
                         [SPECIAL_GLYPH_SIGMA]                   = "S",
-                        [SPECIAL_GLYPH_ARROW_LEFT]              = "<-",
-                        [SPECIAL_GLYPH_ARROW_RIGHT]             = "->",
                         [SPECIAL_GLYPH_ARROW_UP]                = "^",
                         [SPECIAL_GLYPH_ARROW_DOWN]              = "v",
+                        [SPECIAL_GLYPH_ARROW_LEFT]              = "<-",
+                        [SPECIAL_GLYPH_ARROW_RIGHT]             = "->",
                         [SPECIAL_GLYPH_ELLIPSIS]                = "...",
                         [SPECIAL_GLYPH_EXTERNAL_LINK]           = "[LNK]",
                         [SPECIAL_GLYPH_ECSTATIC_SMILEY]         = ":-]",
@@ -73,7 +76,12 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_RECYCLING]               = "~",
                         [SPECIAL_GLYPH_DOWNLOAD]                = "\\",
                         [SPECIAL_GLYPH_SPARKLES]                = "*",
+                        [SPECIAL_GLYPH_LOW_BATTERY]             = "!",
                         [SPECIAL_GLYPH_WARNING_SIGN]            = "!",
+                        [SPECIAL_GLYPH_RED_CIRCLE]              = "o",
+                        [SPECIAL_GLYPH_YELLOW_CIRCLE]           = "o",
+                        [SPECIAL_GLYPH_BLUE_CIRCLE]             = "o",
+                        [SPECIAL_GLYPH_GREEN_CIRCLE]            = "o",
                 },
 
                 /* UTF-8 */
@@ -87,6 +95,8 @@ const char *special_glyph(SpecialGlyph code) {
 
                         /* Single glyphs in both cases */
                         [SPECIAL_GLYPH_VERTICAL_DOTTED]         = u8"┆",
+                        [SPECIAL_GLYPH_HORIZONTAL_DOTTED]       = u8"┄",
+                        [SPECIAL_GLYPH_HORIZONTAL_FAT]          = u8"━",
                         [SPECIAL_GLYPH_TRIANGULAR_BULLET]       = u8"‣",
                         [SPECIAL_GLYPH_BLACK_CIRCLE]            = u8"●",
                         [SPECIAL_GLYPH_WHITE_CIRCLE]            = u8"○",
@@ -98,6 +108,7 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_CROSS_MARK]              = u8"✗",        /* actually called: BALLOT X */
                         [SPECIAL_GLYPH_LIGHT_SHADE]             = u8"░",
                         [SPECIAL_GLYPH_DARK_SHADE]              = u8"▒",
+                        [SPECIAL_GLYPH_FULL_BLOCK]              = u8"█",
                         [SPECIAL_GLYPH_SIGMA]                   = u8"Σ",
                         [SPECIAL_GLYPH_ARROW_UP]                = u8"↑",       /* actually called: UPWARDS ARROW */
                         [SPECIAL_GLYPH_ARROW_DOWN]              = u8"↓",       /* actually called: DOWNWARDS ARROW */
@@ -131,7 +142,15 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_RECYCLING]               = u8"♻️",        /* actually called: UNIVERSAL RECYCLNG SYMBOL */
                         [SPECIAL_GLYPH_DOWNLOAD]                = u8"⤵️",        /* actually called: RIGHT ARROW CURVING DOWN */
                         [SPECIAL_GLYPH_SPARKLES]                = u8"✨",
+                        [SPECIAL_GLYPH_LOW_BATTERY]             = u8"🪫",
                         [SPECIAL_GLYPH_WARNING_SIGN]            = u8"⚠️",
+                        [SPECIAL_GLYPH_COMPUTER_DISK]           = u8"💽",
+                        [SPECIAL_GLYPH_WORLD]                   = u8"🌍",
+
+                        [SPECIAL_GLYPH_RED_CIRCLE]              = u8"🔴",
+                        [SPECIAL_GLYPH_YELLOW_CIRCLE]           = u8"🟡",
+                        [SPECIAL_GLYPH_BLUE_CIRCLE]             = u8"🔵",
+                        [SPECIAL_GLYPH_GREEN_CIRCLE]            = u8"🟢",
                 },
         };
 
@@ -139,5 +158,5 @@ const char *special_glyph(SpecialGlyph code) {
                 return NULL;
 
         assert(code < _SPECIAL_GLYPH_MAX);
-        return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
+        return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
 }
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h
index b6463962..db8dbbff 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.h
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.h
@@ -13,6 +13,8 @@ typedef enum SpecialGlyph {
         SPECIAL_GLYPH_TREE_SPACE,
         SPECIAL_GLYPH_TREE_TOP,
         SPECIAL_GLYPH_VERTICAL_DOTTED,
+        SPECIAL_GLYPH_HORIZONTAL_DOTTED,
+        SPECIAL_GLYPH_HORIZONTAL_FAT,
         SPECIAL_GLYPH_TRIANGULAR_BULLET,
         SPECIAL_GLYPH_BLACK_CIRCLE,
         SPECIAL_GLYPH_WHITE_CIRCLE,
@@ -22,14 +24,15 @@ typedef enum SpecialGlyph {
         SPECIAL_GLYPH_MU,
         SPECIAL_GLYPH_CHECK_MARK,
         SPECIAL_GLYPH_CROSS_MARK,
-        SPECIAL_GLYPH_ARROW_LEFT,
-        SPECIAL_GLYPH_ARROW_RIGHT,
-        SPECIAL_GLYPH_ARROW_UP,
-        SPECIAL_GLYPH_ARROW_DOWN,
-        SPECIAL_GLYPH_ELLIPSIS,
         SPECIAL_GLYPH_LIGHT_SHADE,
         SPECIAL_GLYPH_DARK_SHADE,
+        SPECIAL_GLYPH_FULL_BLOCK,
         SPECIAL_GLYPH_SIGMA,
+        SPECIAL_GLYPH_ARROW_UP,
+        SPECIAL_GLYPH_ARROW_DOWN,
+        SPECIAL_GLYPH_ARROW_LEFT,
+        SPECIAL_GLYPH_ARROW_RIGHT,
+        SPECIAL_GLYPH_ELLIPSIS,
         SPECIAL_GLYPH_EXTERNAL_LINK,
         _SPECIAL_GLYPH_FIRST_EMOJI,
         SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
@@ -44,15 +47,26 @@ typedef enum SpecialGlyph {
         SPECIAL_GLYPH_RECYCLING,
         SPECIAL_GLYPH_DOWNLOAD,
         SPECIAL_GLYPH_SPARKLES,
+        SPECIAL_GLYPH_LOW_BATTERY,
         SPECIAL_GLYPH_WARNING_SIGN,
+        SPECIAL_GLYPH_COMPUTER_DISK,
+        SPECIAL_GLYPH_WORLD,
+        SPECIAL_GLYPH_RED_CIRCLE,
+        SPECIAL_GLYPH_YELLOW_CIRCLE,
+        SPECIAL_GLYPH_BLUE_CIRCLE,
+        SPECIAL_GLYPH_GREEN_CIRCLE,
         _SPECIAL_GLYPH_MAX,
         _SPECIAL_GLYPH_INVALID = -EINVAL,
 } SpecialGlyph;
 
-const char *special_glyph(SpecialGlyph code) _const_;
-
 bool emoji_enabled(void);
 
+const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
+
+static inline const char *special_glyph(SpecialGlyph code) {
+        return special_glyph_full(code, false);
+}
+
 static inline const char *special_glyph_check_mark(bool b) {
         return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
 }
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c
index eed30f09..d20ca3c3 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -22,7 +22,6 @@ DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
                      char, string_hash_func, string_compare_func, free,
                      char*, strv_free);
 
-#if 0 /* NM_IGNORED */
 void path_hash_func(const char *q, struct siphash *state) {
         bool add_slash = false;
 
@@ -36,7 +35,7 @@ void path_hash_func(const char *q, struct siphash *state) {
 
         /* if path is absolute, add one "/" to the hash. */
         if (path_is_absolute(q))
-                siphash24_compress("/", 1, state);
+                siphash24_compress_byte('/', state);
 
         for (;;) {
                 const char *e;
@@ -68,10 +67,9 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
 DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
                      char, path_hash_func, path_compare, free,
                      void, free);
-#endif /* NM_IGNORED */
 
 void trivial_hash_func(const void *p, struct siphash *state) {
-        siphash24_compress(&p, sizeof(p), state);
+        siphash24_compress_typesafe(p, state);
 }
 
 int trivial_compare_func(const void *a, const void *b) {
@@ -97,7 +95,7 @@ const struct hash_ops trivial_hash_ops_free_free = {
 };
 
 void uint64_hash_func(const uint64_t *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(uint64_t), state);
+        siphash24_compress_typesafe(*p, state);
 }
 
 int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
@@ -109,7 +107,7 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func
 #if 0 /* NM_IGNORED */
 #if SIZEOF_DEV_T != 8
 void devt_hash_func(const dev_t *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(dev_t), state);
+        siphash24_compress_typesafe(*p, state);
 }
 #endif
 
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index be642892..3804e94d 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -93,14 +93,14 @@ extern const struct hash_ops trivial_hash_ops;
 extern const struct hash_ops trivial_hash_ops_free;
 extern const struct hash_ops trivial_hash_ops_free_free;
 
-/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
+/* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
  * values indirectly, since they don't fit in a pointer. */
 void uint64_hash_func(const uint64_t *p, struct siphash *state);
 int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
 extern const struct hash_ops uint64_hash_ops;
 
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
- * 64bit archs. Yuck! */
+/* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
+ * 64-bit archs. Yuck! */
 #if SIZEOF_DEV_T != 8
 void devt_hash_func(const dev_t *p, struct siphash *state);
 #else
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 356200cf..9686af0d 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -23,6 +23,7 @@
 #include "random-util.h"
 #include "set.h"
 #include "siphash24.h"
+#include "sort-util.h"
 #include "string-util.h"
 #include "strv.h"
 
@@ -176,9 +177,9 @@ struct _packed_ indirect_storage {
 };
 
 struct direct_storage {
-        /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
-         * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
-         *              or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
+        /* This gives us 39 bytes on 64-bit, or 35 bytes on 32-bit.
+         * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64-bit,
+         *              or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32-bit. */
         uint8_t storage[sizeof(struct indirect_storage)];
 };
 
@@ -2112,3 +2113,91 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl
 
         return set_fnmatch_one(include_patterns, needle);
 }
+
+static int hashmap_entry_compare(
+                struct hashmap_base_entry * const *a,
+                struct hashmap_base_entry * const *b,
+                compare_func_t compare) {
+
+        assert(a && *a);
+        assert(b && *b);
+        assert(compare);
+
+        return compare((*a)->key, (*b)->key);
+}
+
+static int _hashmap_dump_entries_sorted(
+                HashmapBase *h,
+                void ***ret,
+                size_t *ret_n) {
+        _cleanup_free_ void **entries = NULL;
+        Iterator iter;
+        unsigned idx;
+        size_t n = 0;
+
+        assert(ret);
+        assert(ret_n);
+
+        if (_hashmap_size(h) == 0) {
+                *ret = NULL;
+                *ret_n = 0;
+                return 0;
+        }
+
+        /* We append one more element than needed so that the resulting array can be used as a strv. We
+         * don't count this entry in the returned size. */
+        entries = new(void*, _hashmap_size(h) + 1);
+        if (!entries)
+                return -ENOMEM;
+
+        HASHMAP_FOREACH_IDX(idx, h, iter)
+                entries[n++] = bucket_at(h, idx);
+
+        assert(n == _hashmap_size(h));
+        entries[n] = NULL;
+
+        typesafe_qsort_r((struct hashmap_base_entry**) entries, n,
+                         hashmap_entry_compare, h->hash_ops->compare);
+
+        *ret = TAKE_PTR(entries);
+        *ret_n = n;
+        return 0;
+}
+
+int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+        _cleanup_free_ void **entries = NULL;
+        size_t n;
+        int r;
+
+        r = _hashmap_dump_entries_sorted(h, &entries, &n);
+        if (r < 0)
+                return r;
+
+        /* Reuse the array. */
+        FOREACH_ARRAY(e, entries, n)
+                *e = (void*) (*(struct hashmap_base_entry**) e)->key;
+
+        *ret = TAKE_PTR(entries);
+        if (ret_n)
+                *ret_n = n;
+        return 0;
+}
+
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+        _cleanup_free_ void **entries = NULL;
+        size_t n;
+        int r;
+
+        r = _hashmap_dump_entries_sorted(h, &entries, &n);
+        if (r < 0)
+                return r;
+
+        /* Reuse the array. */
+        FOREACH_ARRAY(e, entries, n)
+                *e = entry_value(h, *(struct hashmap_base_entry**) e);
+
+        *ret = TAKE_PTR(entries);
+        if (ret_n)
+                *ret_n = n;
+        return 0;
+}
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index 68d9b81c..49d9d118 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -39,8 +39,8 @@ typedef struct IteratedCache IteratedCache;   /* Caches the iterated order of on
  * by hashmap users, so the definition has to be here. Do not use its fields
  * directly. */
 typedef struct {
-        unsigned idx;         /* index of an entry to be iterated next */
         const void *next_key; /* expected value of that entry's key pointer */
+        unsigned idx;         /* index of an entry to be iterated next */
 #if ENABLE_DEBUG_HASHMAP
         unsigned put_count;   /* hashmap's put_count recorded at start of iteration */
         unsigned rem_count;   /* hashmap's rem_count in previous iteration */
@@ -398,12 +398,36 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
         return _hashmap_get_strv(HASHMAP_BASE(h));
 }
 
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
+static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+
+int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
+static inline int hashmap_dump_keys_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+
 /*
  * Hashmaps are iterated in unpredictable order.
  * OrderedHashmaps are an exception to this. They are iterated in the order
  * the entries were inserted.
  * It is safe to remove the current entry.
  */
+#define _HASHMAP_BASE_FOREACH(e, h, i) \
+        for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), NULL); )
+#define HASHMAP_BASE_FOREACH(e, h) \
+        _HASHMAP_BASE_FOREACH(e, h, UNIQ_T(i, UNIQ))
+
 #define _HASHMAP_FOREACH(e, h, i) \
         for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); )
 #define HASHMAP_FOREACH(e, h) \
@@ -414,6 +438,11 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
 #define ORDERED_HASHMAP_FOREACH(e, h) \
         _ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
 
+#define _HASHMAP_BASE_FOREACH_KEY(e, k, h, i) \
+        for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
+#define HASHMAP_BASE_FOREACH_KEY(e, k, h) \
+        _HASHMAP_BASE_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
+
 #define _HASHMAP_FOREACH_KEY(e, k, h, i) \
         for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
 #define HASHMAP_FOREACH_KEY(e, k, h) \
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c
index d41d2ea0..41228520 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.c
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c
@@ -116,7 +116,7 @@ int unhexmem_full(
                 const char *p,
                 size_t l,
                 bool secure,
-                void **ret,
+                void **ret_data,
                 size_t *ret_len) {
 
         _cleanup_free_ uint8_t *buf = NULL;
@@ -157,8 +157,8 @@ int unhexmem_full(
 
         if (ret_len)
                 *ret_len = (size_t) (z - buf);
-        if (ret)
-                *ret = TAKE_PTR(buf);
+        if (ret_data)
+                *ret_data = TAKE_PTR(buf);
 
         return 0;
 }
@@ -557,12 +557,12 @@ int unbase64char(char c) {
 
         offset += '9' - '0' + 1;
 
-        if (c == '+')
+        if (IN_SET(c, '+', '-')) /* Support both the regular and the URL safe character set (see above) */
                 return offset;
 
         offset++;
 
-        if (c == '/')
+        if (IN_SET(c, '/', '_')) /* ditto */
                 return offset;
 
         return -EINVAL;
@@ -772,7 +772,7 @@ int unbase64mem_full(
                 const char *p,
                 size_t l,
                 bool secure,
-                void **ret,
+                void **ret_data,
                 size_t *ret_size) {
 
         _cleanup_free_ uint8_t *buf = NULL;
@@ -860,8 +860,8 @@ int unbase64mem_full(
 
         if (ret_size)
                 *ret_size = (size_t) (z - buf);
-        if (ret)
-                *ret = TAKE_PTR(buf);
+        if (ret_data)
+                *ret_data = TAKE_PTR(buf);
 
         return 0;
 }
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index 319b21a1..0a10af3e 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -18,9 +18,9 @@ char hexchar(int x) _const_;
 int unhexchar(char c) _const_;
 
 char *hexmem(const void *p, size_t l);
-int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
-static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
-        return unhexmem_full(p, l, false, mem, len);
+int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
+        return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
 }
 
 char base32hexchar(int x) _const_;
@@ -45,9 +45,9 @@ ssize_t base64_append(
                 size_t l,
                 size_t margin,
                 size_t width);
-int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
-static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
-        return unbase64mem_full(p, l, false, mem, len);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
+        return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
 }
 
 void hexdump(FILE *f, const void *p, size_t s);
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c
index b863aec3..310e057a 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -93,14 +93,26 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) {
                 be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001);
 }
 
+bool in4_addr_is_multicast(const struct in_addr *a) {
+        assert(a);
+
+        return IN_MULTICAST(be32toh(a->s_addr));
+}
+
+bool in6_addr_is_multicast(const struct in6_addr *a) {
+        assert(a);
+
+        return IN6_IS_ADDR_MULTICAST(a);
+}
+
 int in_addr_is_multicast(int family, const union in_addr_union *u) {
         assert(u);
 
         if (family == AF_INET)
-                return IN_MULTICAST(be32toh(u->in.s_addr));
+                return in4_addr_is_multicast(&u->in);
 
         if (family == AF_INET6)
-                return IN6_IS_ADDR_MULTICAST(&u->in6);
+                return in6_addr_is_multicast(&u->in6);
 
         return -EAFNOSUPPORT;
 }
@@ -736,10 +748,11 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen)
         }
 }
 
-int in4_addr_prefix_covers(
+int in4_addr_prefix_covers_full(
                 const struct in_addr *prefix,
                 unsigned char prefixlen,
-                const struct in_addr *address) {
+                const struct in_addr *address,
+                unsigned char address_prefixlen) {
 
         struct in_addr masked_prefix, masked_address;
         int r;
@@ -747,6 +760,9 @@ int in4_addr_prefix_covers(
         assert(prefix);
         assert(address);
 
+        if (prefixlen > address_prefixlen)
+                return false;
+
         masked_prefix = *prefix;
         r = in4_addr_mask(&masked_prefix, prefixlen);
         if (r < 0)
@@ -760,10 +776,11 @@ int in4_addr_prefix_covers(
         return in4_addr_equal(&masked_prefix, &masked_address);
 }
 
-int in6_addr_prefix_covers(
+int in6_addr_prefix_covers_full(
                 const struct in6_addr *prefix,
                 unsigned char prefixlen,
-                const struct in6_addr *address) {
+                const struct in6_addr *address,
+                unsigned char address_prefixlen) {
 
         struct in6_addr masked_prefix, masked_address;
         int r;
@@ -771,6 +788,9 @@ int in6_addr_prefix_covers(
         assert(prefix);
         assert(address);
 
+        if (prefixlen > address_prefixlen)
+                return false;
+
         masked_prefix = *prefix;
         r = in6_addr_mask(&masked_prefix, prefixlen);
         if (r < 0)
@@ -784,20 +804,21 @@ int in6_addr_prefix_covers(
         return in6_addr_equal(&masked_prefix, &masked_address);
 }
 
-int in_addr_prefix_covers(
+int in_addr_prefix_covers_full(
                 int family,
                 const union in_addr_union *prefix,
                 unsigned char prefixlen,
-                const union in_addr_union *address) {
+                const union in_addr_union *address,
+                unsigned char address_prefixlen) {
 
         assert(prefix);
         assert(address);
 
         switch (family) {
         case AF_INET:
-                return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
+                return in4_addr_prefix_covers_full(&prefix->in, prefixlen, &address->in, address_prefixlen);
         case AF_INET6:
-                return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
+                return in6_addr_prefix_covers_full(&prefix->in6, prefixlen, &address->in6, address_prefixlen);
         default:
                 return -EAFNOSUPPORT;
         }
@@ -922,12 +943,19 @@ int in_addr_prefix_from_string_auto_internal(
 
 }
 
+void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
+        assert(u);
+        assert(state);
+
+        siphash24_compress(u->bytes, FAMILY_ADDRESS_SIZE(family), state);
+}
+
 void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
         assert(a);
         assert(state);
 
-        siphash24_compress(&a->family, sizeof(a->family), state);
-        siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
+        siphash24_compress_typesafe(a->family, state);
+        in_addr_hash_func(&a->address, a->family, state);
 }
 
 int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
@@ -960,7 +988,7 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
         assert(addr);
         assert(state);
 
-        siphash24_compress(addr, sizeof(*addr), state);
+        siphash24_compress_typesafe(*addr, state);
 }
 
 int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h
index 200b9eb6..5c820c6e 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -40,6 +40,8 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
         return in_addr_data_is_null(a);
 }
 
+bool in4_addr_is_multicast(const struct in_addr *a);
+bool in6_addr_is_multicast(const struct in6_addr *a);
 int in_addr_is_multicast(int family, const union in_addr_union *u);
 
 bool in4_addr_is_link_local(const struct in_addr *a);
@@ -144,9 +146,18 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
 int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
 int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
 int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
-int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
-int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
-int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
+int in4_addr_prefix_covers_full(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address, unsigned char address_prefixlen);
+int in6_addr_prefix_covers_full(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address, unsigned char address_prefixlen);
+int in_addr_prefix_covers_full(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address, unsigned char address_prefixlen);
+static inline int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address) {
+        return in4_addr_prefix_covers_full(prefix, prefixlen, address, 32);
+}
+static inline int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address) {
+        return in6_addr_prefix_covers_full(prefix, prefixlen, address, 128);
+}
+static inline int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address) {
+        return in_addr_prefix_covers_full(family, prefix, prefixlen, address, family == AF_INET ? 32 : family == AF_INET6 ? 128 : 0);
+}
 int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
 int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
 
@@ -176,6 +187,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
  * See also oss-fuzz#11344. */
 #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
 
+void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state);
 void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
 int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
 void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
@@ -186,6 +198,16 @@ extern const struct hash_ops in_addr_data_hash_ops_free;
 extern const struct hash_ops in6_addr_hash_ops;
 extern const struct hash_ops in6_addr_hash_ops_free;
 
+static inline void PTR_TO_IN4_ADDR(const void *p, struct in_addr *ret) {
+        assert(ret);
+        ret->s_addr = (uint32_t) ((uintptr_t) p);
+}
+
+static inline void* IN4_ADDR_TO_PTR(const struct in_addr *a) {
+        assert(a);
+        return (void*) ((uintptr_t) a->s_addr);
+}
+
 #define IPV4_ADDRESS_FMT_STR     "%u.%u.%u.%u"
 #define IPV4_ADDRESS_FMT_VAL(address)              \
         be32toh((address).s_addr) >> 24,           \
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c
index 59e03e62..c748bf1b 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.c
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.c
@@ -6,6 +6,43 @@
 #include "inotify-util.h"
 #include "stat-util.h"
 
+bool inotify_event_next(
+                union inotify_event_buffer *buffer,
+                size_t size,
+                struct inotify_event **iterator,
+                int log_level) {
+
+        struct inotify_event *e;
+        size_t offset = 0;
+
+        assert(buffer);
+        assert(iterator);
+
+        if (*iterator) {
+                assert((uint8_t*) *iterator >= buffer->raw);
+                offset = (uint8_t*) *iterator - buffer->raw;
+                offset += offsetof(struct inotify_event, name) + (*iterator)->len;
+        }
+
+        if (size == offset)
+                return false; /* reached end of list */
+
+        if (size < offset ||
+            size - offset < offsetof(struct inotify_event, name)) {
+                log_full(log_level, "Received invalid inotify event, ignoring.");
+                return false;
+        }
+
+        e = CAST_ALIGN_PTR(struct inotify_event, buffer->raw + offset);
+        if (size - offset - offsetof(struct inotify_event, name) < e->len) {
+                log_full(log_level, "Received invalid inotify event, ignoring.");
+                return false;
+        }
+
+        *iterator = e;
+        return true;
+}
+
 int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
         int wd, r;
 
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h
index 61951ff3..665fdaca 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.h
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.h
@@ -10,29 +10,27 @@
 
 #define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
 
-#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end)    \
-        for (struct inotify_event                                       \
-                     *start = &((buffer).ev),                           \
-                     *end = (struct inotify_event*) ((uint8_t*) start + (sz)), \
-                     *e = start;                                        \
-             (size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \
-             ((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \
-              (log_full(log_level, "Received invalid inotify event, ignoring."), false)); \
-             e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len))
-
-#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level)           \
-        _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ))
+/* This evaluates arguments multiple times */
+#define FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level)            \
+        for (struct inotify_event *e = NULL;                            \
+             inotify_event_next(&buffer, sz, &e, log_level); )
 
 #define FOREACH_INOTIFY_EVENT(e, buffer, sz)                    \
-        _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
+        FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
 
 #define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz)               \
-        _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
+        FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
 
 union inotify_event_buffer {
         struct inotify_event ev;
         uint8_t raw[INOTIFY_EVENT_MAX];
 };
 
+bool inotify_event_next(
+                union inotify_event_buffer *buffer,
+                size_t size,
+                struct inotify_event **iterator,
+                int log_level);
+
 int inotify_add_watch_fd(int fd, int what, uint32_t mask);
 int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c
index 0c480091..abe61ed5 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.c
+++ b/src/libnm-systemd-shared/src/basic/io-util.c
@@ -7,7 +7,9 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include "errno-util.h"
 #include "io-util.h"
+#include "iovec-util.h"
 #include "string-util.h"
 #include "time-util.h"
 
@@ -58,8 +60,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
 
         assert(fd >= 0);
 
-        /* If called with nbytes == 0, let's call read() at least
-         * once, to validate the operation */
+        /* If called with nbytes == 0, let's call read() at least once, to validate the operation */
 
         if (nbytes > (size_t) SSIZE_MAX)
                 return -EINVAL;
@@ -111,13 +112,29 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
 }
 
 #if 0 /* NM_IGNORED */
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
-        const uint8_t *p = ASSERT_PTR(buf);
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
+        const uint8_t *p;
+        usec_t end;
+        int r;
 
         assert(fd >= 0);
+        assert(buf || nbytes == 0);
+
+        if (nbytes == 0) {
+                static const dummy_t dummy[0];
+                assert_cc(sizeof(dummy) == 0);
+                p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
+        } else {
+                if (nbytes == SIZE_MAX)
+                        nbytes = strlen(buf);
+                else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
+                        return -EINVAL;
+
+                p = buf;
+        }
 
-        if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
-                return -EINVAL;
+        /* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
+        end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
 
         do {
                 ssize_t k;
@@ -127,16 +144,31 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
                         if (errno == EINTR)
                                 continue;
 
-                        if (errno == EAGAIN && do_poll) {
-                                /* We knowingly ignore any return value here,
-                                 * and expect that any error/EOF is reported
-                                 * via write() */
+                        if (errno != EAGAIN || timeout == 0)
+                                return -errno;
 
-                                (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
-                                continue;
+                        usec_t wait_for;
+
+                        if (timeout == USEC_INFINITY)
+                                wait_for = USEC_INFINITY;
+                        else {
+                                usec_t t = now(CLOCK_MONOTONIC);
+                                if (t >= end)
+                                        return -ETIME;
+
+                                wait_for = usec_sub_unsigned(end, t);
                         }
 
-                        return -errno;
+                        r = fd_wait_for_event(fd, POLLOUT, wait_for);
+                        if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
+                                /* If timeout == USEC_INFINITY we knowingly ignore any return value
+                                 * here, and expect that any error/EOF is reported via write() */
+                                continue;
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                return -ETIME;
+                        continue;
                 }
 
                 if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
@@ -260,7 +292,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
                                         return -EIO;
                         }
 
-                        if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
+                        if (lseek(fd, n, SEEK_CUR) < 0)
                                 return -errno;
 
                         q += n;
@@ -281,102 +313,4 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
 
         return q - (const uint8_t*) p;
 }
-
-char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
-        char *x;
-
-        x = strjoin(field, value);
-        if (x)
-                iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
-        return x;
-}
-
-char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
-        char *x;
-
-        x = set_iovec_string_field(iovec, n_iovec, field, value);
-        free(value);
-        return x;
-}
-
-struct iovec_wrapper *iovw_new(void) {
-        return malloc0(sizeof(struct iovec_wrapper));
-}
-
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
-        if (free_vectors)
-                for (size_t i = 0; i < iovw->count; i++)
-                        free(iovw->iovec[i].iov_base);
-
-        iovw->iovec = mfree(iovw->iovec);
-        iovw->count = 0;
-}
-
-struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
-        iovw_free_contents(iovw, true);
-
-        return mfree(iovw);
-}
-
-struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
-        iovw_free_contents(iovw, false);
-
-        return mfree(iovw);
-}
-
-int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
-        if (iovw->count >= IOV_MAX)
-                return -E2BIG;
-
-        if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1))
-                return -ENOMEM;
-
-        iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
-        return 0;
-}
-
-int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
-        _cleanup_free_ char *x = NULL;
-        int r;
-
-        x = strjoin(field, value);
-        if (!x)
-                return -ENOMEM;
-
-        r = iovw_put(iovw, x, strlen(x));
-        if (r >= 0)
-                TAKE_PTR(x);
-
-        return r;
-}
-
-int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
-        _cleanup_free_ _unused_ char *free_ptr = value;
-
-        return iovw_put_string_field(iovw, field, value);
-}
-
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
-        for (size_t i = 0; i < iovw->count; i++)
-                iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
-}
-
-size_t iovw_size(struct iovec_wrapper *iovw) {
-        size_t n = 0;
-
-        for (size_t i = 0; i < iovw->count; i++)
-                n += iovw->iovec[i].iov_len;
-
-        return n;
-}
-
-void iovec_array_free(struct iovec *iov, size_t n) {
-        if (!iov)
-                return;
-
-        for (size_t i = 0; i < n; i++)
-                free(iov[i].iov_base);
-
-        free(iov);
-}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h
index 3ad82679..e027c1a8 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.h
+++ b/src/libnm-systemd-shared/src/basic/io-util.h
@@ -6,7 +6,6 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <sys/uio.h>
 
 #include "macro.h"
 #include "time-util.h"
@@ -15,7 +14,11 @@ int flush_fd(int fd);
 
 ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
 int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
-int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
+static inline int loop_write(int fd, const void *buf, size_t nbytes) {
+        return loop_write_full(fd, buf, nbytes, 0);
+}
 
 int pipe_eof(int fd);
 
@@ -24,38 +27,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
 
 ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
 
-static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) {
-        size_t r = 0;
-
-        for (size_t j = 0; j < n; j++)
-                r += i[j].iov_len;
-
-        return r;
-}
-
-static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
-        /* Returns true if there is nothing else to send (bytes written cover all of the iovec),
-         * false if there's still work to do. */
-
-        for (size_t j = 0; j < n; j++) {
-                size_t sub;
-
-                if (i[j].iov_len == 0)
-                        continue;
-                if (k == 0)
-                        return false;
-
-                sub = MIN(i[j].iov_len, k);
-                i[j].iov_len -= sub;
-                i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
-                k -= sub;
-        }
-
-        assert(k == 0); /* Anything else would mean that we wrote more bytes than available,
-                         * or the kernel reported writing more bytes than sent. */
-        return true;
-}
-
 static inline bool FILE_SIZE_VALID(uint64_t l) {
         /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than
          * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */
@@ -73,40 +44,3 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
         return FILE_SIZE_VALID(l);
 
 }
-
-#define IOVEC_NULL (struct iovec) {}
-#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
-#define IOVEC_MAKE_STRING(string)               \
-        ({                                      \
-                char *_s = (char*) (string);    \
-                IOVEC_MAKE(_s, strlen(_s));     \
-        })
-
-char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
-char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
-
-struct iovec_wrapper {
-        struct iovec *iovec;
-        size_t count;
-};
-
-struct iovec_wrapper *iovw_new(void);
-struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
-struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
-
-int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
-static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
-        /* Move data into iovw or free on error */
-        int r = iovw_put(iovw, data, len);
-        if (r < 0)
-                free(data);
-        return r;
-}
-
-int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
-int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
-void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
-size_t iovw_size(struct iovec_wrapper *iovw);
-
-void iovec_array_free(struct iovec *iov, size_t n);
diff --git a/src/libnm-systemd-shared/src/basic/iovec-util.h b/src/libnm-systemd-shared/src/basic/iovec-util.h
new file mode 100644
index 00000000..8cfa5717
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/iovec-util.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "alloc-util.h"
+#include "macro.h"
+
+/* An iovec pointing to a single NUL byte */
+#define IOVEC_NUL_BYTE (const struct iovec) {                   \
+                .iov_base = (void*) (const uint8_t[1]) { 0 },   \
+                .iov_len = 1,                                   \
+        }
+
+size_t iovec_total_size(const struct iovec *iovec, size_t n);
+
+bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
+
+/* This accepts both const and non-const pointers */
+#define IOVEC_MAKE(base, len)                                           \
+        (struct iovec) {                                                \
+                .iov_base = (void*) (base),                             \
+                .iov_len = (len),                                       \
+        }
+
+static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
+        assert(iovec);
+        /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */
+        *iovec = IOVEC_MAKE(s, s ? strlen(s) : 0);
+        return iovec;
+}
+
+#define IOVEC_MAKE_STRING(s) \
+        *iovec_make_string(&(struct iovec) {}, s)
+
+#define CONST_IOVEC_MAKE_STRING(s)              \
+        (const struct iovec) {                  \
+                .iov_base = (char*) s,          \
+                .iov_len = STRLEN(s),           \
+        }
+
+static inline void iovec_done(struct iovec *iovec) {
+        /* A _cleanup_() helper that frees the iov_base in the iovec */
+        assert(iovec);
+
+        iovec->iov_base = mfree(iovec->iov_base);
+        iovec->iov_len = 0;
+}
+
+static inline void iovec_done_erase(struct iovec *iovec) {
+        assert(iovec);
+
+        iovec->iov_base = erase_and_free(iovec->iov_base);
+        iovec->iov_len = 0;
+}
+
+static inline bool iovec_is_set(const struct iovec *iovec) {
+        /* Checks if the iovec points to a non-empty chunk of memory */
+        return iovec && iovec->iov_len > 0 && iovec->iov_base;
+}
+
+static inline bool iovec_is_valid(const struct iovec *iovec) {
+        /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */
+        return !iovec || (iovec->iov_base || iovec->iov_len == 0);
+}
+
+char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
+char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
+
+void iovec_array_free(struct iovec *iovec, size_t n_iovec);
+
+static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
+
+        if (a == b)
+                return 0;
+
+        return memcmp_nn(a ? a->iov_base : NULL,
+                         a ? a->iov_len : 0,
+                         b ? b->iov_base : NULL,
+                         b ? b->iov_len : 0);
+}
+
+static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
+        assert(ret);
+
+        if (!iovec_is_set(source))
+                *ret = (struct iovec) {};
+        else {
+                void *p = memdup(source->iov_base, source->iov_len);
+                if (!p)
+                        return NULL;
+
+                *ret = IOVEC_MAKE(p, source->iov_len);
+        }
+
+        return ret;
+}
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index e4e5dff3..10e69541 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -192,6 +192,18 @@
                 _p;                                                     \
         })
 
+#define LIST_CLEAR(name, head, free_func)       \
+        _LIST_CLEAR(name, head, free_func, UNIQ_T(elem, UNIQ))
+
+/* Clear the list, destroying each element with free_func */
+#define _LIST_CLEAR(name, head, free_func, elem)        \
+        ({                                              \
+                typeof(head) elem;                      \
+                while ((elem = LIST_POP(name, head)))   \
+                        free_func(elem);                \
+                head;                                   \
+        })
+
 /* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
  * it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
 #include "macro.h"
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c
index 2f3701eb..95648775 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.c
+++ b/src/libnm-systemd-shared/src/basic/locale-util.c
@@ -19,6 +19,7 @@
 #include "fileio.h"
 #include "hashmap.h"
 #include "locale-util.h"
+#include "missing_syscall.h"
 #include "path-util.h"
 #include "set.h"
 #include "string-table.h"
@@ -223,7 +224,7 @@ int get_locales(char ***ret) {
         locales = set_free(locales);
 
         r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
-        if (r == -ENXIO || r == 0) {
+        if (IN_SET(r, -ENXIO, 0)) {
                 char **a, **b;
 
                 /* Filter out non-UTF-8 locales, because it's 2019, by default */
@@ -262,7 +263,10 @@ bool locale_is_valid(const char *name) {
         if (!filename_is_valid(name))
                 return false;
 
-        if (!string_is_safe(name))
+        /* Locales look like: ll_CC.ENC@variant, where ll and CC are alphabetic, ENC is alphanumeric with
+         * dashes, and variant seems to be alphabetic.
+         * See: https://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html */
+        if (!in_charset(name, ALPHANUMERICAL "_.-@"))
                 return false;
 
         return true;
@@ -282,11 +286,6 @@ int locale_is_installed(const char *name) {
 
         return true;
 }
-
-void init_gettext(void) {
-        setlocale(LC_ALL, "");
-        textdomain(GETTEXT_PACKAGE);
-}
 #endif /* NM_IGNORED */
 
 bool is_locale_utf8(void) {
@@ -300,13 +299,19 @@ bool is_locale_utf8(void) {
         if (cached_answer >= 0)
                 goto out;
 
-        r = getenv_bool_secure("SYSTEMD_UTF8");
+        r = secure_getenv_bool("SYSTEMD_UTF8");
         if (r >= 0) {
                 cached_answer = r;
                 goto out;
         } else if (r != -ENXIO)
                 log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
 
+        /* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
+        if (gettid() != raw_getpid()) {
+                cached_answer = true;
+                goto out;
+        }
+
         if (!setlocale(LC_ALL, "")) {
                 cached_answer = true;
                 goto out;
@@ -345,11 +350,7 @@ out:
 
 #if 0 /* NM_IGNORED */
 void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
-        if (!l)
-                return;
-
-        for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
-                l[i] = mfree(l[i]);
+        free_many_charp(l, _VARIABLE_LC_MAX);
 }
 
 void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.h b/src/libnm-systemd-shared/src/basic/locale-util.h
index 8990cb6a..81fe8d10 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.h
+++ b/src/libnm-systemd-shared/src/basic/locale-util.h
@@ -33,9 +33,8 @@ int get_locales(char ***l);
 bool locale_is_valid(const char *name);
 int locale_is_installed(const char *name);
 
-#define _(String) gettext(String)
+#define _(String) dgettext(GETTEXT_PACKAGE, String)
 #define N_(String) String
-void init_gettext(void);
 
 bool is_locale_utf8(void);
 
diff --git a/src/libnm-systemd-shared/src/basic/lock-util.h b/src/libnm-systemd-shared/src/basic/lock-util.h
index e7744476..91b332f8 100644
--- a/src/libnm-systemd-shared/src/basic/lock-util.h
+++ b/src/libnm-systemd-shared/src/basic/lock-util.h
@@ -34,9 +34,12 @@ void unposix_unlockpp(int **fd);
         _cleanup_(unposix_unlockpp) _unused_ int *CONCATENATE(_cleanup_unposix_unlock_, UNIQ) = &(fd)
 
 typedef enum LockType {
+        LOCK_NONE, /* Don't lock the file descriptor. Useful if you need to conditionally lock a file. */
         LOCK_BSD,
         LOCK_POSIX,
         LOCK_UNPOSIX,
 } LockType;
 
 int lock_generic(int fd, LockType type, int operation);
+
+int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout);
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index eb7b51cb..1b5bc655 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -467,6 +467,9 @@ void log_set_open_when_needed(bool b);
  * stderr, the console or kmsg */
 void log_set_prohibit_ipc(bool b);
 
+void log_set_assert_return_is_critical(bool b);
+bool log_get_assert_return_is_critical(void) _pure_;
+
 int log_dup_console(void);
 
 #if 0 /* NM_IGNORED */
@@ -521,7 +524,7 @@ typedef struct LogRateLimit {
         RateLimit ratelimit;
 } LogRateLimit;
 
-#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...)        \
+#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...)        \
 ({                                                                              \
         int _log_ratelimit_error = (_error);                                    \
         int _log_ratelimit_level = (_level);                                    \
@@ -545,7 +548,7 @@ typedef struct LogRateLimit {
         ({                                                              \
                 int _level = (level), _e = (error);                     \
                 _e = (log_get_max_level() >= LOG_PRI(_level))           \
-                        ? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__) \
+                        ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
                         : -ERRNO_VALUE(_e);                             \
                 _e < 0 ? _e : -ESTRPIPE;                                \
         })
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index ce7350cb..eec8cba6 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -14,7 +14,7 @@
 
 /* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
  * variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
- * our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
+ * our primary use case for this attribute is registration structures placed in named ELF sections which shall not be
  * padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */
 #if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__)
 #define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__))
@@ -84,7 +84,7 @@
 #define REENABLE_WARNING
 #endif
 
-/* automake test harness */
+/* test harness */
 #define EXIT_TEST_SKIP 77
 
 /* builtins */
@@ -96,6 +96,13 @@
 #error "neither int nor long are four bytes long?!?"
 #endif
 
+static inline uint64_t u64_multiply_safe(uint64_t a, uint64_t b) {
+        if (_unlikely_(a != 0 && b > (UINT64_MAX / a)))
+                return 0; /* overflow */
+
+        return a * b;
+}
+
 /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
 static inline unsigned long ALIGN_POWER2(unsigned long u) {
 
@@ -198,7 +205,7 @@ static inline int __coverity_check_and_return__(int condition) {
 /* We override the glibc assert() here. */
 #undef assert
 #ifdef NDEBUG
-#define assert(expr) do {} while (false)
+#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
 #else
 #define assert(expr) assert_message_se(expr, #expr)
 #endif
@@ -304,12 +311,6 @@ static inline int __coverity_check_and_return__(int condition) {
 /* Pointers range from NULL to POINTER_MAX */
 #define POINTER_MAX ((void*) UINTPTR_MAX)
 
-/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses POINTER_MAX as internal marker for EOL. */
-#define FOREACH_POINTER(p, x, ...)                                                       \
-        for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, POINTER_MAX }; \
-             p != (typeof(p)) POINTER_MAX;                                               \
-             p = *(++_l))
-
 #define _FOREACH_ARRAY(i, array, num, m, end)                           \
         for (typeof(array[0]) *i = (array), *end = ({                   \
                                 typeof(num) m = (num);                  \
@@ -319,31 +320,6 @@ static inline int __coverity_check_and_return__(int condition) {
 #define FOREACH_ARRAY(i, array, num)                                    \
         _FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ))
 
-#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func)             \
-        static inline void name(type *p) {                      \
-                func(p);                                        \
-        }
-
-/* When func() returns the void value (NULL, -1, …) of the appropriate type */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
-        static inline void func##p(type *p) {                   \
-                if (*p)                                         \
-                        *p = func(*p);                          \
-        }
-
-/* When func() doesn't return the appropriate type, set variable to empty afterwards.
- * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty)     \
-        static inline void func##p(type *p) {                   \
-                if (*p != (empty)) {                            \
-                        DISABLE_WARNING_ADDRESS;                \
-                        assert(func);                           \
-                        REENABLE_WARNING;                       \
-                        func(*p);                               \
-                        *p = (empty);                           \
-                }                                               \
-        }
-
 #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope)             \
         scope type *name##_ref(type *p) {                       \
                 if (!p)                                         \
@@ -443,13 +419,13 @@ assert_cc(sizeof(dummy_t) == 0);
                 _q && _q > (base) ? &_q[-1] : NULL;      \
         })
 
-/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly
+/* Iterate through each argument passed. All must be the same type as 'entry' or must be implicitly
  * convertible. The iteration variable 'entry' must already be defined. */
-#define VA_ARGS_FOREACH(entry, ...)                                     \
-        _VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), ##__VA_ARGS__)
-#define _VA_ARGS_FOREACH(entry, _entries_, _current_, ...)         \
-        for (typeof(entry) _entries_[] = { __VA_ARGS__ }, *_current_ = _entries_; \
-             ((long)(_current_ - _entries_) < (long)ELEMENTSOF(_entries_)) && ({ entry = *_current_; true; }); \
+#define FOREACH_ARGUMENT(entry, ...)                                     \
+        _FOREACH_ARGUMENT(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__)
+#define _FOREACH_ARGUMENT(entry, _entries_, _current_, _va_sentinel_, ...)      \
+        for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \
+             ((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
              _current_++)
 
 #include "log.h"
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c
index c1e0a742..789e96a9 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.c
+++ b/src/libnm-systemd-shared/src/basic/memory-util.c
@@ -41,3 +41,19 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
         /* Now we know first 16 bytes match, memcmp() with self.  */
         return memcmp(data, p + 16, length) == 0;
 }
+
+void *memdup_reverse(const void *mem, size_t size) {
+        assert(mem);
+        assert(size != 0);
+
+        void *p = malloc(size);
+        if (!p)
+                return NULL;
+
+        uint8_t *p_dst = p;
+        const uint8_t *p_src = mem;
+        for (size_t i = 0, k = size; i < size; i++, k--)
+                p_dst[i] = p_src[k-1];
+
+        return p;
+}
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h
index d26a0918..294aed67 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.h
+++ b/src/libnm-systemd-shared/src/basic/memory-util.h
@@ -12,9 +12,12 @@
 #include "memory-util-fundamental.h"
 
 size_t page_size(void) _pure_;
-#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
-#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
+#define PAGE_ALIGN(l)          ALIGN_TO(l, page_size())
+#define PAGE_ALIGN_U64(l)      ALIGN_TO_U64(l, page_size())
+#define PAGE_ALIGN_DOWN(l)     ALIGN_DOWN(l, page_size())
+#define PAGE_ALIGN_DOWN_U64(l) ALIGN_DOWN_U64(l, page_size())
+#define PAGE_OFFSET(l)         ALIGN_OFFSET(l, page_size())
+#define PAGE_OFFSET_U64(l)     ALIGN_OFFSET_U64(l, page_size())
 
 /* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */
 static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
@@ -47,13 +50,6 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
             ?: CMP(n1, n2);
 }
 
-#define memzero(x,l)                                            \
-        ({                                                      \
-                size_t _l_ = (l);                               \
-                if (_l_ > 0)                                    \
-                        memset(x, 0, _l_);                      \
-        })
-
 #define zero(x) (memzero(&(x), sizeof(x)))
 
 bool memeqbyte(uint8_t byte, const void *data, size_t length);
@@ -112,36 +108,5 @@ static inline void erase_char(char *p) {
         explicit_bzero_safe(p, sizeof(char));
 }
 
-/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
-typedef struct ArrayCleanup {
-        void **parray;
-        size_t *pn;
-        free_array_func_t pfunc;
-} ArrayCleanup;
-
-static inline void array_cleanup(const ArrayCleanup *c) {
-        assert(c);
-
-        assert(!c->parray == !c->pn);
-
-        if (!c->parray)
-                return;
-
-        if (*c->parray) {
-                assert(c->pfunc);
-                c->pfunc(*c->parray, *c->pn);
-                *c->parray = NULL;
-        }
-
-        *c->pn = 0;
-}
-
-#define CLEANUP_ARRAY(array, n, func)                                   \
-        _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
-                .parray = (void**) &(array),                            \
-                .pn = &(n),                                             \
-                .pfunc = (free_array_func_t) ({                         \
-                                void (*_f)(typeof(array[0]) *a, size_t b) = func; \
-                                _f;                                     \
-                        }),                                             \
-        }
+/* Makes a copy of the buffer with reversed order of bytes */
+void *memdup_reverse(const void *mem, size_t size);
diff --git a/src/libnm-systemd-shared/src/basic/missing_fcntl.h b/src/libnm-systemd-shared/src/basic/missing_fcntl.h
index 24b2dc31..3c85befd 100644
--- a/src/libnm-systemd-shared/src/basic/missing_fcntl.h
+++ b/src/libnm-systemd-shared/src/basic/missing_fcntl.h
@@ -69,9 +69,26 @@
 
 /* So O_LARGEFILE is generally implied by glibc, and defined to zero hence, because we only build in LFS
  * mode. However, when invoking fcntl(F_GETFL) the flag is ORed into the result anyway — glibc does not mask
- * it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves. */
+ * it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves.
+ *
+ * The precise definition is arch specific, so we use the values defined in the kernel (note that some
+ * are hexa and others are octal; duplicated as-is from the kernel definitions):
+ * - alpha, arm, arm64, m68k, mips, parisc, powerpc, sparc: each has a specific value;
+ * - others: they use the "generic" value (defined in include/uapi/asm-generic/fcntl.h) */
 #if O_LARGEFILE != 0
 #define RAW_O_LARGEFILE O_LARGEFILE
 #else
-#define RAW_O_LARGEFILE 0100000
+#if defined(__alpha__) || defined(__arm__) || defined(__aarch64__) || defined(__m68k__)
+#define RAW_O_LARGEFILE 0400000
+#elif defined(__mips__)
+#define RAW_O_LARGEFILE 0x2000
+#elif defined(__parisc__) || defined(__hppa__)
+#define RAW_O_LARGEFILE 000004000
+#elif defined(__powerpc__)
+#define RAW_O_LARGEFILE 0200000
+#elif defined(__sparc__)
+#define RAW_O_LARGEFILE 0x40000
+#else
+#define RAW_O_LARGEFILE 00100000
+#endif
 #endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_socket.h b/src/libnm-systemd-shared/src/basic/missing_socket.h
index a4f6836f..ffda7cc6 100644
--- a/src/libnm-systemd-shared/src/basic/missing_socket.h
+++ b/src/libnm-systemd-shared/src/basic/missing_socket.h
@@ -7,7 +7,6 @@
 #if HAVE_LINUX_VM_SOCKETS_H
 #include <linux/vm_sockets.h>
 #else
-#define VMADDR_CID_ANY -1U
 struct sockaddr_vm {
         unsigned short svm_family;
         unsigned short svm_reserved1;
@@ -22,6 +21,26 @@ struct sockaddr_vm {
 #endif /* !HAVE_LINUX_VM_SOCKETS_H */
 #endif /* NM_IGNORED */
 
+#ifndef VMADDR_CID_ANY
+#define VMADDR_CID_ANY -1U
+#endif
+
+#ifndef VMADDR_CID_HYPERVISOR
+#define VMADDR_CID_HYPERVISOR 0U
+#endif
+
+#ifndef VMADDR_CID_LOCAL
+#define VMADDR_CID_LOCAL 1U
+#endif
+
+#ifndef VMADDR_CID_HOST
+#define VMADDR_CID_HOST 2U
+#endif
+
+#ifndef VMADDR_PORT_ANY
+#define VMADDR_PORT_ANY -1U
+#endif
+
 #ifndef AF_VSOCK
 #define AF_VSOCK 40
 #endif
@@ -34,6 +53,10 @@ struct sockaddr_vm {
 #define SO_PEERGROUPS 59
 #endif
 
+#ifndef SO_PEERPIDFD
+#define SO_PEERPIDFD 77
+#endif
+
 #ifndef SO_BINDTOIFINDEX
 #define SO_BINDTOIFINDEX 62
 #endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h
index 8b39d4f4..18a15ab0 100644
--- a/src/libnm-systemd-shared/src/basic/missing_stat.h
+++ b/src/libnm-systemd-shared/src/basic/missing_stat.h
@@ -9,7 +9,7 @@
 #include <linux/stat.h>
 #endif
 
-/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
+/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
 #define STATX_DEFINITION {                      \
         __u32 stx_mask;                         \
         __u32 stx_blksize;                      \
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index 610a7cef..149c5b48 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -34,6 +34,21 @@
 
 /* ======================================================================= */
 
+#if !HAVE_FCHMODAT2
+static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
+#  ifdef __NR_fchmodat2
+        return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
+#  else
+        errno = ENOSYS;
+        return -1;
+#  endif
+}
+
+#  define fchmodat2 missing_fchmodat2
+#endif
+
+/* ======================================================================= */
+
 #if !HAVE_PIVOT_ROOT
 static inline int missing_pivot_root(const char *new_root, const char *put_old) {
         return syscall(__NR_pivot_root, new_root, put_old);
@@ -404,23 +419,14 @@ static inline int missing_execveat(int dirfd, const char *pathname,
 /* ======================================================================= */
 
 #if !HAVE_CLOSE_RANGE
-static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) {
+static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsigned flags) {
 #  ifdef __NR_close_range
         /* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
-         * userspace exclusively uses signed integers for fds. We don't know just yet how glibc is going to
-         * wrap this syscall, but let's assume it's going to be similar to what they do for close(),
-         * i.e. make the same unsigned → signed type change from the raw kernel syscall compared to the
-         * userspace wrapper. There's only one caveat for this: unlike for close() there's the special
-         * UINT_MAX fd value for the 'end_fd' argument. Let's safely map that to -1 here. And let's refuse
-         * any other negative values. */
-        if ((first_fd < 0) || (end_fd < 0 && end_fd != -1)) {
-                errno = -EBADF;
-                return -1;
-        }
-
+         * userspace exclusively uses signed integers for fds. glibc chose to expose it 1:1 however, hence we
+         * do so here too, even if we end up passing signed fds to it most of the time. */
         return syscall(__NR_close_range,
-                       (unsigned) first_fd,
-                       end_fd == -1 ? UINT_MAX : (unsigned) end_fd, /* Of course, the compiler should figure out that this is the identity mapping IRL */
+                       first_fd,
+                       end_fd,
                        flags);
 #  else
         errno = ENOSYS;
@@ -546,6 +552,10 @@ static inline int missing_open_tree(
 
 /* ======================================================================= */
 
+#ifndef MOVE_MOUNT_BENEATH
+#define MOVE_MOUNT_BENEATH 0x00000200
+#endif
+
 #if !HAVE_MOVE_MOUNT
 
 #ifndef MOVE_MOUNT_F_EMPTY_PATH
@@ -662,3 +672,17 @@ static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
 #  define getdents64 missing_getdents64
 #endif
 #endif /* NM_IGNORED */
+
+/* ======================================================================= */
+
+/* glibc does not provide clone() on ia64, only clone2(). Not only that, but it also doesn't provide a
+ * prototype, only the symbol in the shared library (it provides a prototype for clone(), but not the
+ * symbol in the shared library). */
+#if defined(__ia64__)
+int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags, void *arg);
+#define HAVE_CLONE 0
+#else
+/* We know that everywhere else clone() is available, so we don't bother with a meson check (that takes time
+ * at build time) and just define it. Once the kernel drops ia64 support, we can drop this too. */
+#define HAVE_CLONE 1
+#endif
diff --git a/src/libnm-systemd-shared/src/basic/namespace-util.h b/src/libnm-systemd-shared/src/basic/namespace-util.h
new file mode 100644
index 00000000..34cbec3f
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/namespace-util.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/types.h>
+
+typedef enum NamespaceType {
+        NAMESPACE_CGROUP,
+        NAMESPACE_IPC,
+        NAMESPACE_NET,
+        NAMESPACE_MOUNT,
+        NAMESPACE_PID,
+        NAMESPACE_USER,
+        NAMESPACE_UTS,
+        NAMESPACE_TIME,
+        _NAMESPACE_TYPE_MAX,
+        _NAMESPACE_TYPE_INVALID = -EINVAL,
+} NamespaceType;
+
+extern const struct namespace_info {
+        const char *proc_name;
+        const char *proc_path;
+        unsigned int clone_flag;
+} namespace_info[_NAMESPACE_TYPE_MAX + 1];
+
+int namespace_open(
+                pid_t pid,
+                int *ret_pidns_fd,
+                int *ret_mntns_fd,
+                int *ret_netns_fd,
+                int *ret_userns_fd,
+                int *ret_root_fd);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
+
+int fd_is_ns(int fd, unsigned long nsflag);
+
+int detach_mount_namespace(void);
+
+static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
+        /* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
+         * doesn't overflow uid_t. */
+
+        assert_cc((uid_t) -1 > 0); /* verify that uid_t is unsigned */
+
+        if (range <= 0)
+                return false;
+
+        if (shift > (uid_t) -1 - range)
+                return false;
+
+        return true;
+}
+
+int userns_acquire(const char *uid_map, const char *gid_map);
+int netns_acquire(void);
+int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
+
+int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.c b/src/libnm-systemd-shared/src/basic/ordered-set.c
index f402bb5b..ae50070f 100644
--- a/src/libnm-systemd-shared/src/basic/ordered-set.c
+++ b/src/libnm-systemd-shared/src/basic/ordered-set.c
@@ -93,13 +93,16 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s) {
         bool space = false;
         char *p;
 
+        assert(f);
+        assert(field);
+
         if (ordered_set_isempty(s))
                 return;
 
         fputs(field, f);
 
         ORDERED_SET_FOREACH(p, s)
-                fputs_with_space(f, p, NULL, &space);
+                fputs_with_separator(f, p, NULL, &space);
 
         fputc('\n', f);
 }
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index 2b22039c..34a5375f 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -47,6 +47,24 @@ int parse_boolean(const char *v) {
 }
 
 #if 0 /* NM_IGNORED */
+int parse_tristate_full(const char *v, const char *third, int *ret) {
+        int r;
+
+        if (isempty(v) || streq_ptr(v, third)) { /* Empty string is always taken as the third/invalid/auto state */
+                if (ret)
+                        *ret = -1;
+        } else {
+                r = parse_boolean(v);
+                if (r < 0)
+                        return r;
+
+                if (ret)
+                        *ret = r;
+        }
+
+        return 0;
+}
+
 int parse_pid(const char *s, pid_t* ret_pid) {
         unsigned long ul = 0;
         pid_t pid;
@@ -110,8 +128,7 @@ int parse_ifindex(const char *s) {
 
 #if 0 /* NM_IGNORED */
 int parse_mtu(int family, const char *s, uint32_t *ret) {
-        uint64_t u;
-        size_t m;
+        uint64_t u, m;
         int r;
 
         r = parse_size(s, 1024, &u);
@@ -121,10 +138,16 @@ int parse_mtu(int family, const char *s, uint32_t *ret) {
         if (u > UINT32_MAX)
                 return -ERANGE;
 
-        if (family == AF_INET6)
+        switch (family) {
+        case AF_INET:
+                m = IPV4_MIN_MTU; /* This is 68 */
+                break;
+        case AF_INET6:
                 m = IPV6_MIN_MTU; /* This is 1280 */
-        else
-                m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */
+                break;
+        default:
+                m = 0;
+        }
 
         if (u < m)
                 return -ERANGE;
@@ -431,6 +454,21 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
         return 0;
 }
 
+int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) {
+        unsigned v;
+        int r;
+
+        r = safe_atou(s, &v);
+        if (r < 0)
+                return r;
+
+        if (v < min || v > max)
+                return -ERANGE;
+
+        *ret = v;
+        return 0;
+}
+
 int safe_atoi(const char *s, int *ret_i) {
         unsigned base = 0;
         char *x = NULL;
@@ -660,7 +698,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
         return 0;
 }
 
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
         unsigned l, h;
         int r;
 
@@ -668,7 +706,10 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
         if (r < 0)
                 return r;
 
-        if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
+        if (l > 65535 || h > 65535)
+                return -EINVAL;
+
+        if (!allow_zero && (l == 0 || h == 0))
                 return -EINVAL;
 
         if (h < l)
@@ -754,4 +795,23 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
 
         return store_loadavg_fixed_point(i, f, ret);
 }
+
+/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
+ * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
+bool nft_identifier_valid(const char *id) {
+        if (!id)
+                return false;
+
+        size_t len = strlen(id);
+        if (len == 0 || len > 31)
+                return false;
+
+        if (!ascii_isalpha(id[0]))
+                return false;
+
+        for (size_t i = 1; i < len; i++)
+                if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
+                        return false;
+        return true;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h
index c480407c..c12988ef 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -12,6 +12,10 @@
 typedef unsigned long loadavg_t;
 
 int parse_boolean(const char *v) _pure_;
+int parse_tristate_full(const char *v, const char *third, int *ret);
+static inline int parse_tristate(const char *v, int *ret) {
+        return parse_tristate_full(v, NULL, ret);
+}
 int parse_pid(const char *s, pid_t* ret_pid);
 int parse_mode(const char *s, mode_t *ret);
 int parse_ifindex(const char *s);
@@ -30,11 +34,12 @@ int parse_fd(const char *t);
 #define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS)
 
 int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
-
 static inline int safe_atou(const char *s, unsigned *ret_u) {
         return safe_atou_full(s, 0, ret_u);
 }
 
+int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
+
 int safe_atoi(const char *s, int *ret_i);
 int safe_atolli(const char *s, long long int *ret_i);
 
@@ -134,7 +139,7 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
 int parse_nice(const char *p, int *ret);
 
 int parse_ip_port(const char *s, uint16_t *ret);
-int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
+int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero);
 
 int parse_ip_prefix_length(const char *s, int *ret);
 
@@ -152,3 +157,5 @@ int parse_oom_score_adjust(const char *s, int *ret);
  * to a loadavg_t. */
 int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
 int parse_loadavg_fixed_point(const char *s, loadavg_t *ret);
+
+bool nft_identifier_valid(const char *id);
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index a2af9e0c..0e0f53d9 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -43,7 +43,7 @@ int path_split_and_make_absolute(const char *p, char ***ret) {
         return r;
 }
 
-char *path_make_absolute(const char *p, const char *prefix) {
+char* path_make_absolute(const char *p, const char *prefix) {
         assert(p);
 
         /* Makes every item in the list an absolute path by prepending
@@ -135,11 +135,9 @@ int path_make_relative(const char *from, const char *to, char **ret) {
                                         return -ENOMEM;
                         } else {
                                 /* 'to' is inside of 'from'. */
-                                result = strdup(t);
-                                if (!result)
-                                        return -ENOMEM;
-
-                                path_simplify(result);
+                                r = path_simplify_alloc(t, &result);
+                                if (r < 0)
+                                        return r;
 
                                 if (!path_is_valid(result))
                                         return -EINVAL;
@@ -221,6 +219,7 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret
 
         return path_make_relative(from, to, ret);
 }
+#endif /* NM_IGNORED */
 
 char* path_startswith_strv(const char *p, char **set) {
         STRV_FOREACH(s, set) {
@@ -234,6 +233,7 @@ char* path_startswith_strv(const char *p, char **set) {
         return NULL;
 }
 
+#if 0 /* NM_IGNORED */
 int path_strv_make_absolute_cwd(char **l) {
         int r;
 
@@ -255,7 +255,7 @@ int path_strv_make_absolute_cwd(char **l) {
         return 0;
 }
 
-char **path_strv_resolve(char **l, const char *root) {
+char** path_strv_resolve(char **l, const char *root) {
         unsigned k = 0;
         bool enomem = false;
         int r;
@@ -336,7 +336,7 @@ char **path_strv_resolve(char **l, const char *root) {
         return l;
 }
 
-char **path_strv_resolve_uniq(char **l, const char *root) {
+char** path_strv_resolve_uniq(char **l, const char *root) {
 
         if (strv_isempty(l))
                 return l;
@@ -348,9 +348,9 @@ char **path_strv_resolve_uniq(char **l, const char *root) {
 }
 #endif /* NM_IGNORED */
 
-char *path_simplify(char *path) {
-        bool add_slash = false;
-        char *f = ASSERT_PTR(path);
+char* path_simplify_full(char *path, PathSimplifyFlags flags) {
+        bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
+        char *f = path;
         int r;
 
         /* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
@@ -358,13 +358,17 @@ char *path_simplify(char *path) {
          *
          * ///foo//./bar/.   becomes /foo/bar
          * .//./foo//./bar/. becomes foo/bar
+         * /../foo/bar       becomes /foo/bar
+         * /../foo/bar/..    becomes /foo/bar/..
          */
 
         if (isempty(path))
                 return path;
 
-        if (path_is_absolute(path))
-                f++;
+        keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
+
+        absolute = path_is_absolute(path);
+        f += absolute;  /* Keep leading /, if present. */
 
         for (const char *p = f;;) {
                 const char *e;
@@ -373,11 +377,17 @@ char *path_simplify(char *path) {
                 if (r == 0)
                         break;
 
+                if (r > 0 && absolute && beginning && path_startswith(e, ".."))
+                        /* If we're at the beginning of an absolute path, we can safely skip ".." */
+                        continue;
+
+                beginning = false;
+
                 if (add_slash)
                         *f++ = '/';
 
                 if (r < 0) {
-                        /* if path is invalid, then refuse to simplify remaining part. */
+                        /* if path is invalid, then refuse to simplify the remaining part. */
                         memmove(f, p, strlen(p) + 1);
                         return path;
                 }
@@ -392,11 +402,14 @@ char *path_simplify(char *path) {
         if (f == path)
                 *f++ = '.';
 
+        if (*(f-1) != '/' && keep_trailing_slash)
+                *f++ = '/';
+
         *f = '\0';
         return path;
 }
 
-char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
+char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
         assert(path);
         assert(prefix);
 
@@ -489,10 +502,6 @@ int path_compare(const char *a, const char *b) {
         }
 }
 
-bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
-        return path_equal(a, b) || inode_same(a, b, flags) > 0;
-}
-
 int path_compare_filename(const char *a, const char *b) {
         _cleanup_free_ char *fa = NULL, *fb = NULL;
         int r, j, k;
@@ -661,7 +670,14 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
         return 0;
 }
 
-int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
+int find_executable_full(
+                const char *name,
+                const char *root,
+                char **exec_search_path,
+                bool use_path_envvar,
+                char **ret_filename,
+                int *ret_fd) {
+
         int last_error = -ENOENT, r = 0;
         const char *p = NULL;
 
@@ -812,7 +828,7 @@ int fsck_exists_for_fstype(const char *fstype) {
 }
 #endif /* NM_IGNORED */
 
-static const char *skip_slash_or_dot(const char *p) {
+static const char* skip_slash_or_dot(const char *p) {
         for (; !isempty(p); p++) {
                 if (*p == '/')
                         continue;
@@ -896,7 +912,7 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char **
         return len;
 }
 
-static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
+static const char* skip_slash_or_dot_backward(const char *path, const char *q) {
         assert(path);
         assert(!q || q >= path);
 
@@ -1005,7 +1021,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
         return len;
 }
 
-const char *last_path_component(const char *path) {
+const char* last_path_component(const char *path) {
 
         /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
          *
@@ -1126,17 +1142,19 @@ int path_extract_directory(const char *path, char **ret) {
         if (!path_is_valid(a))
                 return -EINVAL;
 
-        *ret = TAKE_PTR(a);
+        if (ret)
+                *ret = TAKE_PTR(a);
+
         return 0;
 }
 
-bool filename_is_valid(const char *p) {
+bool filename_part_is_valid(const char *p) {
         const char *e;
 
-        if (isempty(p))
-                return false;
+        /* Checks f the specified string is OK to be *part* of a filename. This is different from
+         * filename_is_valid() as "." and ".." and "" are OK by this call, but not by filename_is_valid(). */
 
-        if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
+        if (!p)
                 return false;
 
         e = strchrnul(p, '/');
@@ -1149,6 +1167,17 @@ bool filename_is_valid(const char *p) {
         return true;
 }
 
+bool filename_is_valid(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
+                return false;
+
+        return filename_part_is_valid(p);
+}
+
 bool path_is_valid_full(const char *p, bool accept_dot_dot) {
         if (isempty(p))
                 return false;
@@ -1265,9 +1294,16 @@ bool hidden_or_backup_file(const char *filename) {
 bool is_device_path(const char *path) {
 
         /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
-         * /dev. */
+         * /dev. This accepts any path that starts with /dev/ or /sys/ and has something after that prefix.
+         * It does not actually resolve the path.
+         *
+         * Examples:
+         * /dev/sda, /dev/sda/foo, /sys/class, /dev/.., /sys/.., /./dev/foo → yes.
+         * /../dev/sda, /dev, /sys, /usr/path, /usr/../dev/sda → no.
+         */
 
-        return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
+        const char *p = PATH_STARTSWITH_SET(ASSERT_PTR(path), "/dev/", "/sys/");
+        return !isempty(p);
 }
 
 bool valid_device_node_path(const char *path) {
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index fee6e8ee..5bb51ff5 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -6,6 +6,7 @@
 #include <stddef.h>
 
 #include "macro.h"
+#include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
@@ -25,24 +26,14 @@
 #  define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
 #endif
 
-#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
-#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
-#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/")
-#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/")
+#define DEFAULT_PATH PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
+#define DEFAULT_PATH_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
 #define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/")
 
-#if HAVE_SPLIT_USR
-#  define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
-#  define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR
-#else
-#  define DEFAULT_PATH DEFAULT_PATH_NORMAL
-#  define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR
-#endif
-#endif /* NM_IGNORED */
-
 #ifndef DEFAULT_USER_PATH
 #  define DEFAULT_USER_PATH DEFAULT_PATH
 #endif
+#endif /* NM_IGNORED */
 
 static inline bool is_path(const char *p) {
         if (!p) /* A NULL pointer is definitely not a path */
@@ -64,7 +55,7 @@ int safe_getcwd(char **ret);
 int path_make_absolute_cwd(const char *p, char **ret);
 int path_make_relative(const char *from, const char *to, char **ret);
 int path_make_relative_parent(const char *from_child, const char *to, char **ret);
-char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
+char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
 static inline char* path_startswith(const char *path, const char *prefix) {
         return path_startswith_full(path, prefix, true);
 }
@@ -79,13 +70,38 @@ static inline bool path_equal_filename(const char *a, const char *b) {
         return path_compare_filename(a, b) == 0;
 }
 
-bool path_equal_or_inode_same(const char *a, const char *b, int flags);
+static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
+        return path_equal(a, b) || inode_same(a, b, flags) > 0;
+}
 
 char* path_extend_internal(char **x, ...);
 #define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
 #define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
 
-char* path_simplify(char *path);
+typedef enum PathSimplifyFlags {
+        PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
+} PathSimplifyFlags;
+
+char* path_simplify_full(char *path, PathSimplifyFlags flags);
+static inline char* path_simplify(char *path) {
+        return path_simplify_full(path, 0);
+}
+
+static inline int path_simplify_alloc(const char *path, char **ret) {
+        assert(ret);
+
+        if (!path) {
+                *ret = NULL;
+                return 0;
+        }
+
+        char *t = strdup(path);
+        if (!t)
+                return -ENOMEM;
+
+        *ret = path_simplify(t);
+        return 0;
+}
 
 static inline bool path_equal_ptr(const char *a, const char *b) {
         return !!a == !!b && (!a || path_equal(a, b));
@@ -142,7 +158,7 @@ int fsck_exists_for_fstype(const char *fstype);
                 char *_p, *_n;                                          \
                 size_t _l;                                              \
                 while (_path[0] == '/' && _path[1] == '/')              \
-                        _path ++;                                       \
+                        _path++;                                        \
                 if (isempty(_root))                                     \
                         _ret = _path;                                   \
                 else {                                                  \
@@ -161,10 +177,11 @@ int fsck_exists_for_fstype(const char *fstype);
 
 int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
 int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
-const char *last_path_component(const char *path);
+const char* last_path_component(const char *path);
 int path_extract_filename(const char *path, char **ret);
 int path_extract_directory(const char *path, char **ret);
 
+bool filename_part_is_valid(const char *p) _pure_;
 bool filename_is_valid(const char *p) _pure_;
 bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
 static inline bool path_is_valid(const char *p) {
@@ -197,7 +214,7 @@ static inline const char *skip_dev_prefix(const char *p) {
 }
 
 bool empty_or_root(const char *path);
-static inline const char *empty_to_root(const char *path) {
+static inline const char* empty_to_root(const char *path) {
         return isempty(path) ? "/" : path;
 }
 
diff --git a/src/libnm-systemd-shared/src/basic/pidref.h b/src/libnm-systemd-shared/src/basic/pidref.h
new file mode 100644
index 00000000..c440c8b0
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/pidref.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+
+/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */
+typedef struct PidRef {
+        pid_t pid; /* always valid */
+        int fd;    /* only valid if pidfd are available in the kernel, and we manage to get an fd */
+} PidRef;
+
+#define PIDREF_NULL (const PidRef) { .fd = -EBADF }
+
+/* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
+ * pidref_set_pid() which does so *with* acquiring one, see below) */
+#define PIDREF_MAKE_FROM_PID(x) (PidRef) { .pid = (x), .fd = -EBADF }
+
+static inline bool pidref_is_set(const PidRef *pidref) {
+        return pidref && pidref->pid > 0;
+}
+
+static inline bool pidref_equal(const PidRef *a, const PidRef *b) {
+
+        if (pidref_is_set(a)) {
+                if (!pidref_is_set(b))
+                        return false;
+
+                return a->pid == b->pid;
+        }
+
+        return !pidref_is_set(b);
+}
+
+/* This turns a pid_t into a PidRef structure, and acquires a pidfd for it, if possible. (As opposed to
+ * PIDREF_MAKE_FROM_PID() above, which does not acquire a pidfd.) */
+int pidref_set_pid(PidRef *pidref, pid_t pid);
+int pidref_set_pidstr(PidRef *pidref, const char *pid);
+int pidref_set_pidfd(PidRef *pidref, int fd);
+int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
+int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
+int pidref_set_parent(PidRef *ret);
+static inline int pidref_set_self(PidRef *pidref) {
+        return pidref_set_pid(pidref, 0);
+}
+
+bool pidref_is_self(const PidRef *pidref);
+
+void pidref_done(PidRef *pidref);
+PidRef *pidref_free(PidRef *pidref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free);
+
+int pidref_copy(const PidRef *pidref, PidRef *dest);
+int pidref_dup(const PidRef *pidref, PidRef **ret);
+
+int pidref_new_from_pid(pid_t pid, PidRef **ret);
+
+int pidref_kill(const PidRef *pidref, int sig);
+int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
+int pidref_sigqueue(const PidRef *pidref, int sig, int value);
+
+int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options);
+int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret);
+
+static inline void pidref_done_sigkill_wait(PidRef *pidref) {
+        if (!pidref_is_set(pidref))
+                return;
+
+        (void) pidref_kill(pidref, SIGKILL);
+        (void) pidref_wait_for_terminate(pidref, NULL);
+        pidref_done(pidref);
+}
+
+int pidref_verify(const PidRef *pidref);
+
+#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
+
+extern const struct hash_ops pidref_hash_ops;
+extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */
diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c
index 0af84bd2..b05b08da 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.c
+++ b/src/libnm-systemd-shared/src/basic/prioq.c
@@ -215,7 +215,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
         }
 }
 
-_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
         struct prioq_item *i;
 
         assert(q);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 8601e0da..5dd9003d 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -7,6 +7,7 @@
 #include <limits.h>
 #include <linux/oom.h>
 #include <pthread.h>
+#include <spawn.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -26,6 +27,7 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "argv-util.h"
+#include "dirent-util.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "errno-util.h"
@@ -97,7 +99,7 @@ static int get_process_state(pid_t pid) {
         return (unsigned char) state;
 }
 
-int get_process_comm(pid_t pid, char **ret) {
+int pid_get_comm(pid_t pid, char **ret) {
         _cleanup_free_ char *escaped = NULL, *comm = NULL;
         int r;
 
@@ -135,15 +137,35 @@ int get_process_comm(pid_t pid, char **ret) {
         return 0;
 }
 
-static int get_process_cmdline_nulstr(
+int pidref_get_comm(const PidRef *pid, char **ret) {
+        _cleanup_free_ char *comm = NULL;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_comm(pid->pid, &comm);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = TAKE_PTR(comm);
+        return 0;
+}
+
+static int pid_get_cmdline_nulstr(
                 pid_t pid,
                 size_t max_size,
                 ProcessCmdlineFlags flags,
                 char **ret,
                 size_t *ret_size) {
 
+        _cleanup_free_ char *t = NULL;
         const char *p;
-        char *t;
         size_t k;
         int r;
 
@@ -167,18 +189,17 @@ static int get_process_cmdline_nulstr(
                 return r;
 
         if (k == 0) {
-                t = mfree(t);
-
                 if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
                         return -ENOENT;
 
                 /* Kernel threads have no argv[] */
                 _cleanup_free_ char *comm = NULL;
 
-                r = get_process_comm(pid, &comm);
+                r = pid_get_comm(pid, &comm);
                 if (r < 0)
                         return r;
 
+                free(t);
                 t = strjoin("[", comm, "]");
                 if (!t)
                         return -ENOMEM;
@@ -189,12 +210,15 @@ static int get_process_cmdline_nulstr(
                         t[max_size] = '\0';
         }
 
-        *ret = t;
-        *ret_size = k;
+        if (ret)
+                *ret = TAKE_PTR(t);
+        if (ret_size)
+                *ret_size = k;
+
         return r;
 }
 
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
+int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
         _cleanup_free_ char *t = NULL;
         size_t k;
         char *ans;
@@ -202,7 +226,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
         assert(pid >= 0);
         assert(ret);
 
-        /* Retrieve and format a commandline. See above for discussion of retrieval options.
+        /* Retrieve and format a command line. See above for discussion of retrieval options.
          *
          * There are two main formatting modes:
          *
@@ -216,7 +240,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
          * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
          * PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
 
-        int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k);
+        int full = pid_get_cmdline_nulstr(pid, max_columns, flags, &t, &k);
         if (full < 0)
                 return full;
 
@@ -259,7 +283,27 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
         return 0;
 }
 
-int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
+int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = TAKE_PTR(s);
+        return 0;
+}
+
+int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
         _cleanup_free_ char *t = NULL;
         char **args;
         size_t k;
@@ -269,7 +313,7 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
         assert((flags & ~PROCESS_CMDLINE_COMM_FALLBACK) == 0);
         assert(ret);
 
-        r = get_process_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
+        r = pid_get_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
         if (r < 0)
                 return r;
 
@@ -281,6 +325,27 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
         return 0;
 }
 
+int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret) {
+        _cleanup_strv_free_ char **args = NULL;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_cmdline_strv(pid->pid, flags, &args);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = TAKE_PTR(args);
+
+        return 0;
+}
+
 int container_get_leader(const char *machine, pid_t *pid) {
         _cleanup_free_ char *s = NULL, *class = NULL;
         const char *p;
@@ -322,7 +387,34 @@ int container_get_leader(const char *machine, pid_t *pid) {
         return 0;
 }
 
-int is_kernel_thread(pid_t pid) {
+int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
+        int r;
+
+        assert(ret);
+
+        for (;;) {
+                pid_t ppid;
+
+                r = get_process_ppid(pid, &ppid);
+                if (r < 0)
+                        return r;
+
+                r = in_same_namespace(pid, ppid, type);
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        /* If the parent and the child are not in the same
+                         * namespace, then the child is the leader we are
+                         * looking for. */
+                        *ret = pid;
+                        return 0;
+                }
+
+                pid = ppid;
+        }
+}
+
+int pid_is_kernel_thread(pid_t pid) {
         _cleanup_free_ char *line = NULL;
         unsigned long long flags;
         size_t l, i;
@@ -380,6 +472,23 @@ int is_kernel_thread(pid_t pid) {
         return !!(flags & PF_KTHREAD);
 }
 
+int pidref_is_kernel_thread(const PidRef *pid) {
+        int result, r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        result = pid_is_kernel_thread(pid->pid);
+        if (result < 0)
+                return result;
+
+        r = pidref_verify(pid); /* Verify that the PID wasn't reused since */
+        if (r < 0)
+                return r;
+
+        return result;
+}
+
 int get_process_capeff(pid_t pid, char **ret) {
         const char *p;
         int r;
@@ -405,7 +514,7 @@ static int get_process_link_contents(pid_t pid, const char *proc_file, char **re
         p = procfs_file_alloca(pid, proc_file);
 
         r = readlink_malloc(p, ret);
-        return r == -ENOENT ? -ESRCH : r;
+        return (r == -ENOENT && proc_mounted() > 0) ? -ESRCH : r;
 }
 
 int get_process_exe(pid_t pid, char **ret) {
@@ -449,16 +558,14 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
                 _cleanup_free_ char *line = NULL;
                 char *l;
 
-                r = read_line(f, LONG_LINE_MAX, &line);
+                r = read_stripped_line(f, LONG_LINE_MAX, &line);
                 if (r < 0)
                         return r;
                 if (r == 0)
                         break;
 
-                l = strstrip(line);
-
-                if (startswith(l, field)) {
-                        l += strlen(field);
+                l = startswith(line, field);
+                if (l) {
                         l += strspn(l, WHITESPACE);
 
                         l[strcspn(l, WHITESPACE)] = 0;
@@ -470,7 +577,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
         return -EIO;
 }
 
-int get_process_uid(pid_t pid, uid_t *ret) {
+int pid_get_uid(pid_t pid, uid_t *ret) {
+        assert(ret);
 
         if (pid == 0 || pid == getpid_cached()) {
                 *ret = getuid();
@@ -480,6 +588,26 @@ int get_process_uid(pid_t pid, uid_t *ret) {
         return get_process_id(pid, "Uid:", ret);
 }
 
+int pidref_get_uid(const PidRef *pid, uid_t *ret) {
+        uid_t uid;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_uid(pid->pid, &uid);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = uid;
+        return 0;
+}
+
 int get_process_gid(pid_t pid, gid_t *ret) {
 
         if (pid == 0 || pid == getpid_cached()) {
@@ -606,6 +734,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
         return 0;
 }
 
+int pid_get_start_time(pid_t pid, uint64_t *ret) {
+        _cleanup_free_ char *line = NULL;
+        const char *p;
+        int r;
+
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "stat");
+        r = read_one_line_file(p, &line);
+        if (r == -ENOENT)
+                return -ESRCH;
+        if (r < 0)
+                return r;
+
+        /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+         * value, so let's skip over it manually */
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        unsigned long llu;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*u " /* ppid */
+                   "%*u " /* pgrp */
+                   "%*u " /* session */
+                   "%*u " /* tty_nr */
+                   "%*u " /* tpgid */
+                   "%*u " /* flags */
+                   "%*u " /* minflt */
+                   "%*u " /* cminflt */
+                   "%*u " /* majflt */
+                   "%*u " /* cmajflt */
+                   "%*u " /* utime */
+                   "%*u " /* stime */
+                   "%*u " /* cutime */
+                   "%*u " /* cstime */
+                   "%*i " /* priority */
+                   "%*i " /* nice */
+                   "%*u " /* num_threads */
+                   "%*u " /* itrealvalue */
+                   "%lu ", /* starttime */
+                   &llu) != 1)
+                return -EIO;
+
+        if (ret)
+                *ret = llu;
+
+        return 0;
+}
+
+int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
+        uint64_t t;
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        r = pid_get_start_time(pid->pid, ret ? &t : NULL);
+        if (r < 0)
+                return r;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = t;
+
+        return 0;
+}
+
 int get_process_umask(pid_t pid, mode_t *ret) {
         _cleanup_free_ char *m = NULL;
         const char *p;
@@ -670,7 +874,7 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
         assert(pid > 1);
 
         if (!name) {
-                r = get_process_comm(pid, &buffer);
+                r = pid_get_comm(pid, &buffer);
                 if (r < 0)
                         log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
                 else
@@ -824,7 +1028,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         _cleanup_fclose_ FILE *f = NULL;
         char *value = NULL;
         const char *path;
-        size_t l, sum = 0;
+        size_t sum = 0;
         int r;
 
         assert(pid >= 0);
@@ -859,9 +1063,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         if (r < 0)
                 return r;
 
-        l = strlen(field);
         for (;;) {
                 _cleanup_free_ char *line = NULL;
+                const char *match;
 
                 if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
                         return -ENOBUFS;
@@ -874,8 +1078,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
 
                 sum += r;
 
-                if (strneq(line, field, l) && line[l] == '=') {
-                        value = strdup(line + l + 1);
+                match = startswith(line, field);
+                if (match && *match == '=') {
+                        value = strdup(match + 1);
                         if (!value)
                                 return -ENOMEM;
 
@@ -892,6 +1097,9 @@ int pid_is_my_child(pid_t pid) {
         pid_t ppid;
         int r;
 
+        if (pid < 0)
+                return -ESRCH;
+
         if (pid <= 1)
                 return false;
 
@@ -902,11 +1110,28 @@ int pid_is_my_child(pid_t pid) {
         return ppid == getpid_cached();
 }
 
-bool pid_is_unwaited(pid_t pid) {
+int pidref_is_my_child(const PidRef *pid) {
+        int r, result;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        result = pid_is_my_child(pid->pid);
+        if (result < 0)
+                return result;
+
+        r = pidref_verify(pid);
+        if (r < 0)
+                return r;
+
+        return result;
+}
+
+int pid_is_unwaited(pid_t pid) {
         /* Checks whether a PID is still valid at all, including a zombie */
 
         if (pid < 0)
-                return false;
+                return -ESRCH;
 
         if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
                 return true;
@@ -920,13 +1145,31 @@ bool pid_is_unwaited(pid_t pid) {
         return errno != ESRCH;
 }
 
-bool pid_is_alive(pid_t pid) {
+int pidref_is_unwaited(const PidRef *pid) {
+        int r;
+
+        if (!pidref_is_set(pid))
+                return -ESRCH;
+
+        if (pid->pid == 1 || pidref_is_self(pid))
+                return true;
+
+        r = pidref_kill(pid, 0);
+        if (r == -ESRCH)
+                return false;
+        if (r < 0)
+                return r;
+
+        return true;
+}
+
+int pid_is_alive(pid_t pid) {
         int r;
 
         /* Checks whether a PID is still valid and not a zombie */
 
         if (pid < 0)
-                return false;
+                return -ESRCH;
 
         if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
                 return true;
@@ -935,10 +1178,33 @@ bool pid_is_alive(pid_t pid) {
                 return true;
 
         r = get_process_state(pid);
-        if (IN_SET(r, -ESRCH, 'Z'))
+        if (r == -ESRCH)
                 return false;
+        if (r < 0)
+                return r;
 
-        return true;
+        return r != 'Z';
+}
+
+int pidref_is_alive(const PidRef *pidref) {
+        int r, result;
+
+        if (!pidref_is_set(pidref))
+                return -ESRCH;
+
+        result = pid_is_alive(pidref->pid);
+        if (result < 0) {
+                assert(result != -ESRCH);
+                return result;
+        }
+
+        r = pidref_verify(pidref);
+        if (r == -ESRCH)
+                return false;
+        if (r < 0)
+                return r;
+
+        return result;
 }
 
 int pid_from_same_root_fs(pid_t pid) {
@@ -1043,7 +1309,7 @@ int opinionated_personality(unsigned long *ret) {
         if (current < 0)
                 return current;
 
-        if (((unsigned long) current & 0xffff) == PER_LINUX32)
+        if (((unsigned long) current & OPINIONATED_PERSONALITY_MASK) == PER_LINUX32)
                 *ret = PER_LINUX32;
         else
                 *ret = PER_LINUX;
@@ -1057,7 +1323,10 @@ void valgrind_summary_hack(void) {
                 pid_t pid;
                 pid = raw_clone(SIGCHLD);
                 if (pid < 0)
-                        log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
+                        log_struct_errno(
+                                LOG_EMERG, errno,
+                                "MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
+                                LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
                 else if (pid == 0)
                         exit(EXIT_SUCCESS);
                 else {
@@ -1104,7 +1373,7 @@ pid_t getpid_cached(void) {
          * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
          */
 
-        __atomic_compare_exchange_n(
+        (void) __atomic_compare_exchange_n(
                         &cached_pid,
                         &current_value,
                         CACHED_PID_BUSY,
@@ -1159,10 +1428,55 @@ static void restore_sigsetp(sigset_t **ssp) {
                 (void) sigprocmask(SIG_SETMASK, *ssp, NULL);
 }
 
+pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
+        size_t ps;
+        pid_t pid;
+        void *mystack;
+
+        /* A wrapper around glibc's clone() call that automatically sets up a "nested" stack. Only supports
+         * invocations without CLONE_VM, so that we can continue to use the parent's stack mapping.
+         *
+         * Note: glibc's clone() wrapper does not synchronize malloc() locks. This means that if the parent
+         * is threaded these locks will be in an undefined state in the child, and hence memory allocations
+         * are likely going to run into deadlocks. Hence: if you use this function make sure your parent is
+         * strictly single-threaded or your child never calls malloc(). */
+
+        assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
+                         CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
+
+        /* We allocate some space on the stack to use as the stack for the child (hence "nested"). Note that
+         * the net effect is that the child will have the start of its stack inside the stack of the parent,
+         * but since they are a CoW copy of each other that's fine. We allocate one page-aligned page. But
+         * since we don't want to deal with differences between systems where the stack grows backwards or
+         * forwards we'll allocate one more and place the stack address in the middle. Except that we also
+         * want it page aligned, hence we'll allocate one page more. Makes 3. */
+
+        ps = page_size();
+        mystack = alloca(ps*3);
+        mystack = (uint8_t*) mystack + ps; /* move pointer one page ahead since stacks usually grow backwards */
+        mystack = (void*) ALIGN_TO((uintptr_t) mystack, ps); /* align to page size (moving things further ahead) */
+
+#if HAVE_CLONE
+        pid = clone(fn, mystack, flags, userdata);
+#else
+        pid = __clone2(fn, mystack, ps, flags, userdata);
+#endif
+        if (pid < 0)
+                return -errno;
+
+        return pid;
+}
+
+static int fork_flags_to_signal(ForkFlags flags) {
+        return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
+                (flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
+                                                 SIGKILL;
+}
+
 int safe_fork_full(
                 const char *name,
                 const int stdio_fds[3],
-                const int except_fds[],
+                int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 pid_t *ret_pid) {
@@ -1170,9 +1484,12 @@ int safe_fork_full(
         pid_t original_pid, pid;
         sigset_t saved_ss, ss;
         _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
-        bool block_signals = false, block_all = false;
+        bool block_signals = false, block_all = false, intermediary = false;
         int prio, r;
 
+        assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
+        assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT));
+
         /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
          * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
 
@@ -1185,9 +1502,10 @@ int safe_fork_full(
                 fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
         }
 
-        if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
-                /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
-                 * be sure that SIGTERMs are not lost we might send to the child. */
+        if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) {
+                /* We temporarily block all signals, so that the new child has them blocked initially. This
+                 * way, we can be sure that SIGTERMs are not lost we might send to the child. (Note that for
+                 * FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */
 
                 assert_se(sigfillset(&ss) >= 0);
                 block_signals = block_all = true;
@@ -1206,17 +1524,47 @@ int safe_fork_full(
                 saved_ssp = &saved_ss;
         }
 
-        if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
+        if (FLAGS_SET(flags, FORK_DETACH)) {
+                assert(!FLAGS_SET(flags, FORK_WAIT));
+                assert(!ret_pid);
+
+                /* Fork off intermediary child if needed */
+
+                r = is_reaper_process();
+                if (r < 0)
+                        return log_full_errno(prio, r, "Failed to determine if we are a reaper process: %m");
+
+                if (!r) {
+                        /* Not a reaper process, hence do a double fork() so we are reparented to one */
+
+                        pid = fork();
+                        if (pid < 0)
+                                return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
+                        if (pid > 0) {
+                                log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
+                                return 1; /* return in the parent */
+                        }
+
+                        intermediary = true;
+                }
+        }
+
+        if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
                 pid = raw_clone(SIGCHLD|
                                 (FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
-                                (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
+                                (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
+                                (FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0));
         else
                 pid = fork();
         if (pid < 0)
                 return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
         if (pid > 0) {
-                /* We are in the parent process */
 
+                /* If we are in the intermediary process, exit now */
+                if (intermediary)
+                        _exit(EXIT_SUCCESS);
+
+                /* We are in the parent process */
                 log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
 
                 if (flags & FORK_WAIT) {
@@ -1259,8 +1607,8 @@ int safe_fork_full(
                                        r, "Failed to rename process, ignoring: %m");
         }
 
-        if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT))
-                if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) {
+        if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
+                if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
                         log_full_errno(prio, errno, "Failed to set death signal: %m");
                         _exit(EXIT_FAILURE);
                 }
@@ -1285,7 +1633,7 @@ int safe_fork_full(
                 }
         }
 
-        if (flags & FORK_DEATHSIG) {
+        if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) {
                 pid_t ppid;
                 /* Let's see if the parent PID is still the one we started from? If not, then the parent
                  * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
@@ -1294,8 +1642,9 @@ int safe_fork_full(
                 if (ppid == 0)
                         /* Parent is in a different PID namespace. */;
                 else if (ppid != original_pid) {
-                        log_debug("Parent died early, raising SIGTERM.");
-                        (void) raise(SIGTERM);
+                        int sig = fork_flags_to_signal(flags);
+                        log_debug("Parent died early, raising %s.", signal_to_string(sig));
+                        (void) raise(sig);
                         _exit(EXIT_FAILURE);
                 }
         }
@@ -1328,6 +1677,9 @@ int safe_fork_full(
                                 log_full_errno(prio, r, "Failed to rearrange stdio fds: %m");
                                 _exit(EXIT_FAILURE);
                         }
+
+                        /* Turn off O_NONBLOCK on the fdio fds, in case it was left on */
+                        stdio_disable_nonblock();
                 } else {
                         r = make_null_stdio();
                         if (r < 0) {
@@ -1353,6 +1705,19 @@ int safe_fork_full(
                 }
         }
 
+        if (flags & FORK_PACK_FDS) {
+                /* FORK_CLOSE_ALL_FDS ensures that except_fds are the only FDs >= 3 that are
+                 * open, this is including the log. This is required by pack_fds, which will
+                 * get stuck in an infinite loop of any FDs other than except_fds are open. */
+                assert(FLAGS_SET(flags, FORK_CLOSE_ALL_FDS));
+
+                r = pack_fds(except_fds, n_except_fds);
+                if (r < 0) {
+                        log_full_errno(prio, r, "Failed to pack file descriptors: %m");
+                        _exit(EXIT_FAILURE);
+                }
+        }
+
         if (flags & FORK_CLOEXEC_OFF) {
                 r = fd_cloexec_many(except_fds, n_except_fds, false);
                 if (r < 0) {
@@ -1389,10 +1754,34 @@ int safe_fork_full(
         return 0;
 }
 
+int pidref_safe_fork_full(
+                const char *name,
+                const int stdio_fds[3],
+                int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                PidRef *ret_pid) {
+
+        pid_t pid;
+        int r, q;
+
+        assert(!FLAGS_SET(flags, FORK_WAIT));
+
+        r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
+        if (r < 0)
+                return r;
+
+        q = pidref_set_pid(ret_pid, pid);
+        if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
+                *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+
+        return r;
+}
+
 int namespace_fork(
                 const char *outer_name,
                 const char *inner_name,
-                const int except_fds[],
+                int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 int pidns_fd,
@@ -1411,7 +1800,7 @@ int namespace_fork(
         r = safe_fork_full(outer_name,
                            NULL,
                            except_fds, n_except_fds,
-                           (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+                           (flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
@@ -1633,6 +2022,212 @@ int get_process_threads(pid_t pid) {
         return n;
 }
 
+int is_reaper_process(void) {
+        int b = 0;
+
+        /* Checks if we are running in a reaper process, i.e. if we are expected to deal with processes
+         * reparented to us. This simply checks if we are PID 1 or if PR_SET_CHILD_SUBREAPER was called. */
+
+        if (getpid_cached() == 1)
+                return true;
+
+        if (prctl(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0UL, 0UL, 0UL) < 0)
+                return -errno;
+
+        return b != 0;
+}
+
+int make_reaper_process(bool b) {
+
+        if (getpid_cached() == 1) {
+
+                if (!b)
+                        return -EINVAL;
+
+                return 0;
+        }
+
+        /* Some prctl()s insist that all 5 arguments are specified, others do not. Let's always specify all,
+         * to avoid any ambiguities */
+        if (prctl(PR_SET_CHILD_SUBREAPER, (unsigned long) b, 0UL, 0UL, 0UL) < 0)
+                return -errno;
+
+        return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(posix_spawnattr_t*, posix_spawnattr_destroy, NULL);
+
+int posix_spawn_wrapper(
+                const char *path,
+                char * const *argv,
+                char * const *envp,
+                const char *cgroup,
+                PidRef *ret_pidref) {
+
+        short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
+        posix_spawnattr_t attr;
+        sigset_t mask;
+        int r;
+
+        /* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the
+         * caller will be blocked until the child either exits or exec's. The memory of the child will be
+         * fully shared with the memory of the parent, so that there are no copy-on-write or memory.max
+         * issues.
+         *
+         * Also, move the newly-created process into 'cgroup' through POSIX_SPAWN_SETCGROUP (clone3())
+         * if available. Note that CLONE_INTO_CGROUP is only supported on cgroup v2.
+         * returns 1: We're already in the right cgroup
+         *         0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller
+         *            needs to call 'cg_attach' on their own */
+
+        assert(path);
+        assert(argv);
+        assert(ret_pidref);
+
+        assert_se(sigfillset(&mask) >= 0);
+
+        r = posix_spawnattr_init(&attr);
+        if (r != 0)
+                return -r; /* These functions return a positive errno on failure */
+
+        /* Initialization needs to succeed before we can set up a destructor. */
+        _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
+
+#if HAVE_PIDFD_SPAWN
+        _cleanup_close_ int cgroup_fd = -EBADF;
+
+        if (cgroup) {
+                _cleanup_free_ char *resolved_cgroup = NULL;
+
+                r = cg_get_path_and_check(
+                                SYSTEMD_CGROUP_CONTROLLER,
+                                cgroup,
+                                /* suffix= */ NULL,
+                                &resolved_cgroup);
+                if (r < 0)
+                        return r;
+
+                cgroup_fd = open(resolved_cgroup, O_PATH|O_DIRECTORY|O_CLOEXEC);
+                if (cgroup_fd < 0)
+                        return -errno;
+
+                r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd);
+                if (r != 0)
+                        return -r;
+
+                flags |= POSIX_SPAWN_SETCGROUP;
+        }
+#endif
+
+        r = posix_spawnattr_setflags(&attr, flags);
+        if (r != 0)
+                return -r;
+        r = posix_spawnattr_setsigmask(&attr, &mask);
+        if (r != 0)
+                return -r;
+
+#if HAVE_PIDFD_SPAWN
+        _cleanup_close_ int pidfd = -EBADF;
+
+        r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
+        if (r == 0) {
+                r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
+                if (r < 0)
+                        return r;
+
+                return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
+        }
+        if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
+                return -r;
+
+        /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
+         * flags to remove the cgroup one, which is what redirects to clone3() */
+        flags &= ~POSIX_SPAWN_SETCGROUP;
+        r = posix_spawnattr_setflags(&attr, flags);
+        if (r != 0)
+                return -r;
+#endif
+
+        pid_t pid;
+        r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+        if (r != 0)
+                return -r;
+
+        r = pidref_set_pid(ret_pidref, pid);
+        if (r < 0)
+                return r;
+
+        return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */
+}
+
+int proc_dir_open(DIR **ret) {
+        DIR *d;
+
+        assert(ret);
+
+        d = opendir("/proc");
+        if (!d)
+                return -errno;
+
+        *ret = d;
+        return 0;
+}
+
+int proc_dir_read(DIR *d, pid_t *ret) {
+        assert(d);
+
+        for (;;) {
+                struct dirent *de;
+
+                errno = 0;
+                de = readdir_no_dot(d);
+                if (!de) {
+                        if (errno != 0)
+                                return -errno;
+
+                        break;
+                }
+
+                if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
+                        continue;
+
+                if (parse_pid(de->d_name, ret) >= 0)
+                        return 1;
+        }
+
+        if (ret)
+                *ret = 0;
+        return 0;
+}
+
+int proc_dir_read_pidref(DIR *d, PidRef *ret) {
+        int r;
+
+        assert(d);
+
+        for (;;) {
+                pid_t pid;
+
+                r = proc_dir_read(d, &pid);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                r = pidref_set_pid(ret, pid);
+                if (r == -ESRCH) /* gone by now? skip it */
+                        continue;
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+
+        if (ret)
+                *ret = PIDREF_NULL;
+        return 0;
+}
+
 static const char *const sigchld_code_table[] = {
         [CLD_EXITED] = "exited",
         [CLD_KILLED] = "killed",
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 5cf5c7c6..49350065 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -14,6 +14,7 @@
 #include "alloc-util.h"
 #include "format-util.h"
 #include "macro.h"
+#include "namespace-util.h"
 #include "time-util.h"
 
 #define procfs_file_alloca(pid, field)                                  \
@@ -38,21 +39,29 @@ typedef enum ProcessCmdlineFlags {
         PROCESS_CMDLINE_QUOTE_POSIX   = 1 << 3,
 } ProcessCmdlineFlags;
 
-int get_process_comm(pid_t pid, char **ret);
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
-int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
+int pid_get_comm(pid_t pid, char **ret);
+int pidref_get_comm(const PidRef *pid, char **ret);
+int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
+int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret);
 int get_process_exe(pid_t pid, char **ret);
-int get_process_uid(pid_t pid, uid_t *ret);
+int pid_get_uid(pid_t pid, uid_t *ret);
+int pidref_get_uid(const PidRef *pid, uid_t *ret);
 int get_process_gid(pid_t pid, gid_t *ret);
 int get_process_capeff(pid_t pid, char **ret);
 int get_process_cwd(pid_t pid, char **ret);
 int get_process_root(pid_t pid, char **ret);
 int get_process_environ(pid_t pid, char **ret);
 int get_process_ppid(pid_t pid, pid_t *ret);
+int pid_get_start_time(pid_t pid, uint64_t *ret);
+int pidref_get_start_time(const PidRef* pid, uint64_t *ret);
 int get_process_umask(pid_t pid, mode_t *ret);
 
 int container_get_leader(const char *machine, pid_t *pid);
 
+int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
+
 int wait_for_terminate(pid_t pid, siginfo_t *status);
 
 typedef enum WaitFlags {
@@ -74,13 +83,17 @@ void sigkill_nowaitp(pid_t *pid);
 
 int kill_and_sigcont(pid_t pid, int sig);
 
-int is_kernel_thread(pid_t pid);
+int pid_is_kernel_thread(pid_t pid);
+int pidref_is_kernel_thread(const PidRef *pid);
 
 int getenv_for_pid(pid_t pid, const char *field, char **_value);
 
-bool pid_is_alive(pid_t pid);
-bool pid_is_unwaited(pid_t pid);
+int pid_is_alive(pid_t pid);
+int pidref_is_alive(const PidRef *pidref);
+int pid_is_unwaited(pid_t pid);
+int pidref_is_unwaited(const PidRef *pidref);
 int pid_is_my_child(pid_t pid);
+int pidref_is_my_child(const PidRef *pidref);
 int pid_from_same_root_fs(pid_t pid);
 
 bool is_main_thread(void);
@@ -88,12 +101,17 @@ bool is_main_thread(void);
 bool oom_score_adjust_is_valid(int oa);
 
 #ifndef PERSONALITY_INVALID
-/* personality(7) documents that 0xffffffffUL is used for querying the
+/* personality(2) documents that 0xFFFFFFFFUL is used for querying the
  * current personality, hence let's use that here as error
  * indicator. */
-#define PERSONALITY_INVALID 0xffffffffLU
+#define PERSONALITY_INVALID 0xFFFFFFFFUL
 #endif
 
+/* The personality() syscall returns a 32-bit value where the top three bytes are reserved for flags that
+ * emulate historical or architectural quirks, and only the least significant byte reflects the actual
+ * personality we're interested in. */
+#define OPINIONATED_PERSONALITY_MASK 0xFFUL
+
 unsigned long personality_from_string(const char *p);
 const char *personality_to_string(unsigned long);
 
@@ -141,30 +159,41 @@ void reset_cached_pid(void);
 
 int must_be_root(void);
 
+pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
+
+/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded
+ * programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks,
+ * and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if
+ * the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads
+ * or the child uses malloc(). 💣 */
 typedef enum ForkFlags {
         FORK_RESET_SIGNALS      = 1 <<  0, /* Reset all signal handlers and signal mask */
         FORK_CLOSE_ALL_FDS      = 1 <<  1, /* Close all open file descriptors in the child, except for 0,1,2 */
-        FORK_DEATHSIG           = 1 <<  2, /* Set PR_DEATHSIG in the child to SIGTERM */
+        FORK_DEATHSIG_SIGTERM   = 1 <<  2, /* Set PR_DEATHSIG in the child to SIGTERM */
         FORK_DEATHSIG_SIGINT    = 1 <<  3, /* Set PR_DEATHSIG in the child to SIGINT */
-        FORK_REARRANGE_STDIO    = 1 <<  4, /* Connect 0,1,2 to specified fds or /dev/null */
-        FORK_REOPEN_LOG         = 1 <<  5, /* Reopen log connection */
-        FORK_LOG                = 1 <<  6, /* Log above LOG_DEBUG log level about failures */
-        FORK_WAIT               = 1 <<  7, /* Wait until child exited */
-        FORK_NEW_MOUNTNS        = 1 <<  8, /* Run child in its own mount namespace */
-        FORK_MOUNTNS_SLAVE      = 1 <<  9, /* Make child's mount namespace MS_SLAVE */
-        FORK_PRIVATE_TMP        = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
-        FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
-        FORK_STDOUT_TO_STDERR   = 1 << 12, /* Make stdout a copy of stderr */
-        FORK_FLUSH_STDIO        = 1 << 13, /* fflush() stdout (and stderr) before forking */
-        FORK_NEW_USERNS         = 1 << 14, /* Run child in its own user namespace */
-        FORK_CLOEXEC_OFF        = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
-        FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
+        FORK_DEATHSIG_SIGKILL   = 1 <<  4, /* Set PR_DEATHSIG in the child to SIGKILL */
+        FORK_REARRANGE_STDIO    = 1 <<  5, /* Connect 0,1,2 to specified fds or /dev/null */
+        FORK_REOPEN_LOG         = 1 <<  6, /* Reopen log connection */
+        FORK_LOG                = 1 <<  7, /* Log above LOG_DEBUG log level about failures */
+        FORK_WAIT               = 1 <<  8, /* Wait until child exited */
+        FORK_NEW_MOUNTNS        = 1 <<  9, /* Run child in its own mount namespace                               💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+        FORK_MOUNTNS_SLAVE      = 1 << 10, /* Make child's mount namespace MS_SLAVE */
+        FORK_PRIVATE_TMP        = 1 << 11, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
+        FORK_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+        FORK_STDOUT_TO_STDERR   = 1 << 13, /* Make stdout a copy of stderr */
+        FORK_FLUSH_STDIO        = 1 << 14, /* fflush() stdout (and stderr) before forking */
+        FORK_NEW_USERNS         = 1 << 15, /* Run child in its own user namespace                                💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+        FORK_CLOEXEC_OFF        = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
+        FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
+        FORK_DETACH             = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
+        FORK_NEW_NETNS          = 1 << 19, /* Run child in its own network namespace                             💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
+        FORK_PACK_FDS           = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */
 } ForkFlags;
 
 int safe_fork_full(
                 const char *name,
                 const int stdio_fds[3],
-                const int except_fds[],
+                int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 pid_t *ret_pid);
@@ -173,7 +202,30 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
         return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
 }
 
-int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
+int pidref_safe_fork_full(
+                const char *name,
+                const int stdio_fds[3],
+                int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                PidRef *ret_pid);
+
+static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
+        return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
+}
+
+int namespace_fork(
+                const char *outer_name,
+                const char *inner_name,
+                int except_fds[],
+                size_t n_except_fds,
+                ForkFlags flags,
+                int pidns_fd,
+                int mntns_fd,
+                int netns_fd,
+                int userns_fd,
+                int root_fd,
+                pid_t *ret_pid);
 
 int set_oom_score_adjust(int value);
 int get_oom_score_adjust(int *ret);
@@ -201,3 +253,17 @@ int setpriority_closest(int priority);
 _noreturn_ void freeze(void);
 
 int get_process_threads(pid_t pid);
+
+int is_reaper_process(void);
+int make_reaper_process(bool b);
+
+int posix_spawn_wrapper(
+                const char *path,
+                char * const *argv,
+                char * const *envp,
+                const char *cgroup,
+                PidRef *ret_pidref);
+
+int proc_dir_open(DIR **ret);
+int proc_dir_read(DIR *d, pid_t *ret);
+int proc_dir_read_pidref(DIR *d, PidRef *ret);
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 934d5e25..c7b95516 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -6,7 +6,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/random.h>
-#include <pthread.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -28,6 +27,7 @@
 #include "missing_syscall.h"
 #include "missing_threads.h"
 #include "parse-util.h"
+#include "process-util.h"
 #include "random-util.h"
 #include "sha256.h"
 #include "time-util.h"
@@ -50,7 +50,7 @@ static void fallback_random_bytes(void *p, size_t n) {
                 .call_id = fallback_counter++,
                 .stamp_mono = now(CLOCK_MONOTONIC),
                 .stamp_real = now(CLOCK_REALTIME),
-                .pid = getpid(),
+                .pid = getpid_cached(),
                 .tid = gettid(),
         };
 
@@ -226,7 +226,7 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
                 if (ioctl(fd, RNDADDENTROPY, info) < 0)
                         return -errno;
         } else {
-                r = loop_write(fd, seed, size, false);
+                r = loop_write(fd, seed, size);
                 if (r < 0)
                         return r;
         }
diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h
index bb7160a8..492ea3b4 100644
--- a/src/libnm-systemd-shared/src/basic/ratelimit.h
+++ b/src/libnm-systemd-shared/src/basic/ratelimit.h
@@ -12,6 +12,8 @@ typedef struct RateLimit {
         usec_t begin;
 } RateLimit;
 
+#define RATELIMIT_OFF (const RateLimit) { .interval = USEC_INFINITY, .burst = UINT_MAX }
+
 static inline void ratelimit_reset(RateLimit *rl) {
         rl->num = rl->begin = 0;
 }
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index 270d397d..95c86584 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -21,7 +21,7 @@ int reset_all_signal_handlers(void) {
                 .sa_handler = SIG_DFL,
                 .sa_flags = SA_RESTART,
         };
-        int r = 0;
+        int ret = 0, r;
 
         for (int sig = 1; sig < _NSIG; sig++) {
 
@@ -29,14 +29,14 @@ int reset_all_signal_handlers(void) {
                 if (IN_SET(sig, SIGKILL, SIGSTOP))
                         continue;
 
-                /* On Linux the first two RT signals are reserved by
-                 * glibc, and sigaction() will return EINVAL for them. */
-                if (sigaction(sig, &sa, NULL) < 0)
-                        if (errno != EINVAL && r >= 0)
-                                r = -errno;
+                /* On Linux the first two RT signals are reserved by glibc, and sigaction() will return
+                 * EINVAL for them. */
+                r = RET_NERRNO(sigaction(sig, &sa, NULL));
+                if (r != -EINVAL)
+                        RET_GATHER(ret, r);
         }
 
-        return r;
+        return ret;
 }
 
 int reset_signal_mask(void) {
@@ -60,10 +60,7 @@ int sigaction_many_internal(const struct sigaction *sa, ...) {
                 if (sig == 0)
                         continue;
 
-                if (sigaction(sig, sa, NULL) < 0) {
-                        if (r >= 0)
-                                r = -errno;
-                }
+                RET_GATHER(r, RET_NERRNO(sigaction(sig, sa, NULL)));
         }
 
         va_end(ap);
@@ -90,7 +87,7 @@ static int sigset_add_many_ap(sigset_t *ss, va_list ap) {
         return r;
 }
 
-int sigset_add_many(sigset_t *ss, ...) {
+int sigset_add_many_internal(sigset_t *ss, ...) {
         va_list ap;
         int r;
 
@@ -101,7 +98,7 @@ int sigset_add_many(sigset_t *ss, ...) {
         return r;
 }
 
-int sigprocmask_many(int how, sigset_t *old, ...) {
+int sigprocmask_many_internal(int how, sigset_t *old, ...) {
         va_list ap;
         sigset_t ss;
         int r;
@@ -116,46 +113,43 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
         if (r < 0)
                 return r;
 
-        if (sigprocmask(how, &ss, old) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(sigprocmask(how, &ss, old));
 }
 
 static const char *const static_signal_table[] = {
-        [SIGHUP] = "HUP",
-        [SIGINT] = "INT",
-        [SIGQUIT] = "QUIT",
-        [SIGILL] = "ILL",
-        [SIGTRAP] = "TRAP",
-        [SIGABRT] = "ABRT",
-        [SIGBUS] = "BUS",
-        [SIGFPE] = "FPE",
-        [SIGKILL] = "KILL",
-        [SIGUSR1] = "USR1",
-        [SIGSEGV] = "SEGV",
-        [SIGUSR2] = "USR2",
-        [SIGPIPE] = "PIPE",
-        [SIGALRM] = "ALRM",
-        [SIGTERM] = "TERM",
+        [SIGHUP]    = "HUP",
+        [SIGINT]    = "INT",
+        [SIGQUIT]   = "QUIT",
+        [SIGILL]    = "ILL",
+        [SIGTRAP]   = "TRAP",
+        [SIGABRT]   = "ABRT",
+        [SIGBUS]    = "BUS",
+        [SIGFPE]    = "FPE",
+        [SIGKILL]   = "KILL",
+        [SIGUSR1]   = "USR1",
+        [SIGSEGV]   = "SEGV",
+        [SIGUSR2]   = "USR2",
+        [SIGPIPE]   = "PIPE",
+        [SIGALRM]   = "ALRM",
+        [SIGTERM]   = "TERM",
 #ifdef SIGSTKFLT
         [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
 #endif
-        [SIGCHLD] = "CHLD",
-        [SIGCONT] = "CONT",
-        [SIGSTOP] = "STOP",
-        [SIGTSTP] = "TSTP",
-        [SIGTTIN] = "TTIN",
-        [SIGTTOU] = "TTOU",
-        [SIGURG] = "URG",
-        [SIGXCPU] = "XCPU",
-        [SIGXFSZ] = "XFSZ",
+        [SIGCHLD]   = "CHLD",
+        [SIGCONT]   = "CONT",
+        [SIGSTOP]   = "STOP",
+        [SIGTSTP]   = "TSTP",
+        [SIGTTIN]   = "TTIN",
+        [SIGTTOU]   = "TTOU",
+        [SIGURG]    = "URG",
+        [SIGXCPU]   = "XCPU",
+        [SIGXFSZ]   = "XFSZ",
         [SIGVTALRM] = "VTALRM",
-        [SIGPROF] = "PROF",
-        [SIGWINCH] = "WINCH",
-        [SIGIO] = "IO",
-        [SIGPWR] = "PWR",
-        [SIGSYS] = "SYS"
+        [SIGPROF]   = "PROF",
+        [SIGWINCH]  = "WINCH",
+        [SIGIO]     = "IO",
+        [SIGPWR]    = "PWR",
+        [SIGSYS]    = "SYS"
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h
index ad2ba841..8826fbeb 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.h
+++ b/src/libnm-systemd-shared/src/basic/signal-util.h
@@ -31,8 +31,11 @@ int sigaction_many_internal(const struct sigaction *sa, ...);
 #define sigaction_many(sa, ...)                                         \
         sigaction_many_internal(sa, __VA_ARGS__, -1)
 
-int sigset_add_many(sigset_t *ss, ...);
-int sigprocmask_many(int how, sigset_t *old, ...);
+int sigset_add_many_internal(sigset_t *ss, ...);
+#define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1)
+
+int sigprocmask_many_internal(int how, sigset_t *old, ...);
+#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1)
 
 const char *signal_to_string(int i) _const_;
 int signal_from_string(const char *s) _pure_;
@@ -46,7 +49,7 @@ static inline void block_signals_reset(sigset_t *ss) {
 #define BLOCK_SIGNALS(...)                                                         \
         _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({        \
                 sigset_t _t;                                                       \
-                assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \
+                assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0);     \
                 _t;                                                                \
         })
 
diff --git a/src/libnm-systemd-shared/src/basic/siphash24.h b/src/libnm-systemd-shared/src/basic/siphash24.h
index e46f3cc5..f9867630 100644
--- a/src/libnm-systemd-shared/src/basic/siphash24.h
+++ b/src/libnm-systemd-shared/src/basic/siphash24.h
@@ -52,15 +52,16 @@ siphash24 (const void *in, size_t inlen, const uint8_t k[16])
 void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
 void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
 #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
+#define siphash24_compress_typesafe(in, state)                  \
+        siphash24_compress(&(in), sizeof(typeof(in)), (state))
 
 static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
-        uint8_t i = in;
-
-        siphash24_compress(&i, sizeof i, state);
+        siphash24_compress_byte(in, state);
 }
 
 static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
-        siphash24_compress(&in, sizeof in, state);
+        uint64_t u = htole64(in);
+        siphash24_compress_typesafe(u, state);
 }
 
 static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) {
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 9b411e07..df3e2c17 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -46,6 +46,11 @@
 #  define IDN_FLAGS 0
 #endif
 
+/* From the kernel's include/net/scm.h */
+#ifndef SCM_MAX_FD
+#  define SCM_MAX_FD 253
+#endif
+
 static const char* const socket_address_type_table[] = {
         [SOCK_STREAM] =    "Stream",
         [SOCK_DGRAM] =     "Datagram",
@@ -547,7 +552,7 @@ int sockaddr_pretty(
                         } else {
                                 if (path[path_len - 1] == '\0')
                                         /* We expect a terminating NUL and don't print it */
-                                        path_len --;
+                                        path_len--;
 
                                 p = cescape_length(path, path_len);
                         }
@@ -628,28 +633,33 @@ int getsockname_pretty(int fd, char **ret) {
         return sockaddr_pretty(&sa.sa, salen, false, true, ret);
 }
 
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **ret) {
+        char host[NI_MAXHOST];
         int r;
-        char host[NI_MAXHOST], *ret;
 
-        assert(_ret);
+        assert(sa);
+        assert(salen > sizeof(sa_family_t));
 
-        r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS);
+        r = getnameinfo(sa, salen, host, sizeof(host), /* service= */ NULL, /* service_len= */ 0, IDN_FLAGS);
         if (r != 0) {
-                int saved_errno = errno;
+                if (r == EAI_MEMORY)
+                        return log_oom_debug();
+                if (r == EAI_SYSTEM)
+                        log_debug_errno(errno, "getnameinfo() failed, ignoring: %m");
+                else
+                        log_debug("getnameinfo() failed, ignoring: %s", gai_strerror(r));
 
-                r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
-                if (r < 0)
-                        return r;
+                return sockaddr_pretty(sa, salen, /* translate_ipv6= */ true, /* include_port= */ true, ret);
+        }
 
-                log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
-        } else {
-                ret = strdup(host);
-                if (!ret)
+        if (ret) {
+                char *copy = strdup(host);
+                if (!copy)
                         return -ENOMEM;
+
+                *ret = copy;
         }
 
-        *_ret = ret;
         return 0;
 }
 
@@ -874,13 +884,11 @@ bool address_label_valid(const char *p) {
 int getpeercred(int fd, struct ucred *ucred) {
         socklen_t n = sizeof(struct ucred);
         struct ucred u;
-        int r;
 
         assert(fd >= 0);
         assert(ucred);
 
-        r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
-        if (r < 0)
+        if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0)
                 return -errno;
 
         if (n != sizeof(struct ucred))
@@ -909,8 +917,10 @@ int getpeersec(int fd, char **ret) {
                 if (!s)
                         return -ENOMEM;
 
-                if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+                if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) {
+                        s[n] = 0;
                         break;
+                }
 
                 if (errno != ERANGE)
                         return -errno;
@@ -927,12 +937,16 @@ int getpeersec(int fd, char **ret) {
 }
 
 int getpeergroups(int fd, gid_t **ret) {
-        socklen_t n = sizeof(gid_t) * 64;
+        socklen_t n = sizeof(gid_t) * 64U;
         _cleanup_free_ gid_t *d = NULL;
 
         assert(fd >= 0);
         assert(ret);
 
+        long ngroups_max = sysconf(_SC_NGROUPS_MAX);
+        if (ngroups_max > 0)
+                n = MAX(n, sizeof(gid_t) * (socklen_t) ngroups_max);
+
         for (;;) {
                 d = malloc(n);
                 if (!d)
@@ -950,7 +964,7 @@ int getpeergroups(int fd, gid_t **ret) {
         assert_se(n % sizeof(gid_t) == 0);
         n /= sizeof(gid_t);
 
-        if ((socklen_t) (int) n != n)
+        if (n > INT_MAX)
                 return -E2BIG;
 
         *ret = TAKE_PTR(d);
@@ -958,6 +972,68 @@ int getpeergroups(int fd, gid_t **ret) {
         return (int) n;
 }
 
+int getpeerpidfd(int fd) {
+        socklen_t n = sizeof(int);
+        int pidfd = -EBADF;
+
+        assert(fd >= 0);
+
+        if (getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &pidfd, &n) < 0)
+                return -errno;
+
+        if (n != sizeof(int))
+                return -EIO;
+
+        return pidfd;
+}
+
+ssize_t send_many_fds_iov_sa(
+                int transport_fd,
+                int *fds_array, size_t n_fds_array,
+                const struct iovec *iov, size_t iovlen,
+                const struct sockaddr *sa, socklen_t len,
+                int flags) {
+
+        _cleanup_free_ struct cmsghdr *cmsg = NULL;
+        struct msghdr mh = {
+                .msg_name = (struct sockaddr*) sa,
+                .msg_namelen = len,
+                .msg_iov = (struct iovec *)iov,
+                .msg_iovlen = iovlen,
+        };
+        ssize_t k;
+
+        assert(transport_fd >= 0);
+        assert(fds_array || n_fds_array == 0);
+
+        /* The kernel will reject sending more than SCM_MAX_FD FDs at once */
+        if (n_fds_array > SCM_MAX_FD)
+                return -E2BIG;
+
+        /* We need either an FD array or data to send. If there's nothing, return an error. */
+        if (n_fds_array == 0 && !iov)
+                return -EINVAL;
+
+        if (n_fds_array > 0) {
+                mh.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds_array);
+                mh.msg_control = cmsg = malloc(mh.msg_controllen);
+                if (!cmsg)
+                        return -ENOMEM;
+
+                *cmsg = (struct cmsghdr) {
+                        .cmsg_len = CMSG_LEN(sizeof(int) * n_fds_array),
+                        .cmsg_level = SOL_SOCKET,
+                        .cmsg_type = SCM_RIGHTS,
+                };
+                memcpy(CMSG_DATA(cmsg), fds_array, sizeof(int) * n_fds_array);
+        }
+        k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
+        if (k < 0)
+                return (ssize_t) -errno;
+
+        return k;
+}
+
 ssize_t send_one_fd_iov_sa(
                 int transport_fd,
                 int fd,
@@ -1013,6 +1089,78 @@ int send_one_fd_sa(
         return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags);
 }
 
+ssize_t receive_many_fds_iov(
+                int transport_fd,
+                struct iovec *iov, size_t iovlen,
+                int **ret_fds_array, size_t *ret_n_fds_array,
+                int flags) {
+
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * SCM_MAX_FD)) control;
+        struct msghdr mh = {
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+                .msg_iov = iov,
+                .msg_iovlen = iovlen,
+        };
+        _cleanup_free_ int *fds_array = NULL;
+        size_t n_fds_array = 0;
+        struct cmsghdr *cmsg;
+        ssize_t k;
+
+        assert(transport_fd >= 0);
+        assert(ret_fds_array);
+        assert(ret_n_fds_array);
+
+        /*
+         * Receive many FDs via @transport_fd. We don't care for the transport-type. We retrieve all the FDs
+         * at once. This is best used in combination with send_many_fds().
+         */
+
+        k = recvmsg_safe(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags);
+        if (k < 0)
+                return k;
+
+        CMSG_FOREACH(cmsg, &mh)
+                if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+                        size_t n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+                        fds_array = GREEDY_REALLOC(fds_array, n_fds_array + n);
+                        if (!fds_array) {
+                                cmsg_close_all(&mh);
+                                return -ENOMEM;
+                        }
+
+                        memcpy(fds_array + n_fds_array, CMSG_TYPED_DATA(cmsg, int), sizeof(int) * n);
+                        n_fds_array += n;
+                }
+
+        if (n_fds_array == 0) {
+                cmsg_close_all(&mh);
+
+                /* If didn't receive an FD or any data, return an error. */
+                if (k == 0)
+                        return -EIO;
+        }
+
+        *ret_fds_array = TAKE_PTR(fds_array);
+        *ret_n_fds_array = n_fds_array;
+
+        return k;
+}
+
+int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags) {
+        ssize_t k;
+
+        k = receive_many_fds_iov(transport_fd, NULL, 0, ret_fds_array, ret_n_fds_array, flags);
+        if (k == 0)
+                return 0;
+
+        /* k must be negative, since receive_many_fds_iov() only returns a positive value if data was received
+         * through the iov. */
+        assert(k < 0);
+        return (int) k;
+}
+
 ssize_t receive_one_fd_iov(
                 int transport_fd,
                 struct iovec *iov, size_t iovlen,
@@ -1189,7 +1337,7 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
         assert(buf_len > 0);
 
         /* This is similar to cmsg_find_data(), but copy the found data to buf. This should be typically used
-         * when reading possibly unaligned data such as timestamp, as time_t is 64bit and size_t is 32bit on
+         * when reading possibly unaligned data such as timestamp, as time_t is 64-bit and size_t is 32-bit on
          * RISCV32. See issue #27241. */
 
         cmsg = cmsg_find(mh, level, type, CMSG_LEN(buf_len));
@@ -1532,6 +1680,50 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s) {
 }
 
 #if 0 /* NM_IGNORED */
+int vsock_parse_port(const char *s, unsigned *ret) {
+        int r;
+
+        assert(ret);
+
+        if (!s)
+                return -EINVAL;
+
+        unsigned u;
+        r = safe_atou(s, &u);
+        if (r < 0)
+                return r;
+
+        /* Port 0 is apparently valid and not special in AF_VSOCK (unlike on IP). But VMADDR_PORT_ANY
+         * (UINT32_MAX) is. Hence refuse that. */
+
+        if (u == VMADDR_PORT_ANY)
+                return -EINVAL;
+
+        *ret = u;
+        return 0;
+}
+
+int vsock_parse_cid(const char *s, unsigned *ret) {
+        assert(ret);
+
+        if (!s)
+                return -EINVAL;
+
+        /* Parsed an AF_VSOCK "CID". This is a 32bit entity, and the usual type is "unsigned". We recognize
+         * the three special CIDs as strings, and otherwise parse the numeric CIDs. */
+
+        if (streq(s, "hypervisor"))
+                *ret = VMADDR_CID_HYPERVISOR;
+        else if (streq(s, "local"))
+                *ret = VMADDR_CID_LOCAL;
+        else if (streq(s, "host"))
+                *ret = VMADDR_CID_HOST;
+        else
+                return safe_atou(s, ret);
+
+        return 0;
+}
+
 int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
         /* AF_VSOCK socket in vsock:cid:port notation */
         _cleanup_free_ char *n = NULL;
@@ -1557,7 +1749,7 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
         if (!e)
                 return -EINVAL;
 
-        r = safe_atou(e+1, &port);
+        r = vsock_parse_port(e+1, &port);
         if (r < 0)
                 return r;
 
@@ -1568,15 +1760,15 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
         if (isempty(n))
                 cid = VMADDR_CID_ANY;
         else {
-                r = safe_atou(n, &cid);
+                r = vsock_parse_cid(n, &cid);
                 if (r < 0)
                         return r;
         }
 
         *ret_address = (SocketAddress) {
                 .sockaddr.vm = {
-                        .svm_cid = cid,
                         .svm_family = AF_VSOCK,
+                        .svm_cid = cid,
                         .svm_port = port,
                 },
                 .type = type,
@@ -1585,4 +1777,19 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
 
         return 0;
 }
+
+int vsock_get_local_cid(unsigned *ret) {
+        _cleanup_close_ int vsock_fd = -EBADF;
+
+        assert(ret);
+
+        vsock_fd = open("/dev/vsock", O_RDONLY|O_CLOEXEC);
+        if (vsock_fd < 0)
+                return log_debug_errno(errno, "Failed to open /dev/vsock: %m");
+
+        if (ioctl(vsock_fd, IOCTL_VM_SOCKETS_GET_LOCAL_CID, ret) < 0)
+                return log_debug_errno(errno, "Failed to query local AF_VSOCK CID: %m");
+
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index 26f9636f..15c7d1c5 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -115,7 +115,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
 int getpeername_pretty(int fd, bool include_port, char **ret);
 int getsockname_pretty(int fd, char **ret);
 
-int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
+int socknameinfo_pretty(const struct sockaddr *sa, socklen_t salen, char **_ret);
 
 const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
 SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
@@ -154,7 +154,30 @@ bool address_label_valid(const char *p);
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
 int getpeergroups(int fd, gid_t **ret);
+int getpeerpidfd(int fd);
 
+ssize_t send_many_fds_iov_sa(
+                int transport_fd,
+                int *fds_array, size_t n_fds_array,
+                const struct iovec *iov, size_t iovlen,
+                const struct sockaddr *sa, socklen_t len,
+                int flags);
+static inline ssize_t send_many_fds_iov(
+                int transport_fd,
+                int *fds_array, size_t n_fds_array,
+                const struct iovec *iov, size_t iovlen,
+                int flags) {
+
+        return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, iov, iovlen, NULL, 0, flags);
+}
+static inline int send_many_fds(
+                int transport_fd,
+                int *fds_array,
+                size_t n_fds_array,
+                int flags) {
+
+        return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, NULL, 0, NULL, 0, flags);
+}
 ssize_t send_one_fd_iov_sa(
                 int transport_fd,
                 int fd,
@@ -169,6 +192,8 @@ int send_one_fd_sa(int transport_fd,
 #define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
 ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
 int receive_one_fd(int transport_fd, int flags);
+ssize_t receive_many_fds_iov(int transport_fd, struct iovec *iov, size_t iovlen, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
+int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_array, int flags);
 
 ssize_t next_datagram_size_fd(int fd);
 
@@ -181,7 +206,7 @@ int flush_accept(int fd);
  * at compile time, that the requested type has a smaller or same alignment as 'struct cmsghdr', and one
  * during runtime, that the actual pointer matches the alignment too. This is supposed to catch cases such as
  * 'struct timeval' is embedded into 'struct cmsghdr' on architectures where the alignment of the former is 8
- * bytes (because of a 64bit time_t), but of the latter is 4 bytes (because size_t is 32bit), such as
+ * bytes (because of a 64-bit time_t), but of the latter is 4 bytes (because size_t is 32 bits), such as
  * riscv32. */
 #define CMSG_TYPED_DATA(cmsg, type)                                     \
         ({                                                              \
@@ -296,7 +321,7 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
 int socket_bind_to_ifname(int fd, const char *ifname);
 int socket_bind_to_ifindex(int fd, int ifindex);
 
-/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
+/* Define a 64-bit version of timeval/timespec in any case, even on 32-bit userspace. */
 struct timeval_large {
         uint64_t tvl_sec, tvl_usec;
 };
@@ -304,7 +329,7 @@ struct timespec_large {
         uint64_t tvl_sec, tvl_nsec;
 };
 
-/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
+/* glibc duplicates timespec/timeval on certain 32-bit arches, once in 32-bit and once in 64-bit.
  * See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
  * to prevent from recvmsg_safe() returning -EXFULL. */
 #define CMSG_SPACE_TIMEVAL                                              \
@@ -353,6 +378,14 @@ int socket_get_mtu(int fd, int af, size_t *ret);
 
 int connect_unix_path(int fd, int dir_fd, const char *path);
 
+static inline bool VSOCK_CID_IS_REGULAR(unsigned cid) {
+        /* 0, 1, 2, UINT32_MAX are special, refuse those */
+        return cid > 2 && cid < UINT32_MAX;
+}
+
+int vsock_parse_port(const char *s, unsigned *ret);
+int vsock_parse_cid(const char *s, unsigned *ret);
+
 /* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in
  * src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of
  * protocol mismatch. */
@@ -365,3 +398,5 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
  * /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
  * authoritative. */
 #define SOMAXCONN_DELUXE INT_MAX
+
+int vsock_get_local_cid(unsigned *ret);
diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h
index 52d611b8..9c818bd7 100644
--- a/src/libnm-systemd-shared/src/basic/sort-util.h
+++ b/src/libnm-systemd-shared/src/basic/sort-util.h
@@ -18,7 +18,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
         ({                                                              \
                 const typeof((b)[0]) *_k = k;                           \
                 int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*, typeof(userdata)) = func; \
-                xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
+                (typeof((b)[0])*) xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
         })
 
 /**
@@ -38,7 +38,7 @@ static inline void* bsearch_safe(const void *key, const void *base,
         ({                                                              \
                 const typeof((b)[0]) *_k = k;                           \
                 int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*) = func; \
-                bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
+                (typeof((b)[0])*) bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
         })
 
 /**
@@ -61,7 +61,6 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison
                 _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
         })
 
-#if 0 /* NM_IGNORED */
 static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
         if (nmemb <= 1)
                 return;
@@ -75,6 +74,6 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso
                 int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \
                 qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
         })
-#endif /* NM_IGNORED */
 
 int cmp_int(const int *a, const int *b);
+int cmp_uint16(const uint16_t *a, const uint16_t *b);
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index a81ee468..4da3845b 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -27,46 +27,130 @@
 #include "stat-util.h"
 #include "string-util.h"
 
-#if 0 /* NM_IGNORED */
-int is_symlink(const char *path) {
-        struct stat info;
+static int verify_stat_at(
+                int fd,
+                const char *path,
+                bool follow,
+                int (*verify_func)(const struct stat *st),
+                bool verify) {
+        struct stat st;
+        int r;
 
-        assert(path);
+        assert(fd >= 0 || fd == AT_FDCWD);
+        assert(!isempty(path) || !follow);
+        assert(verify_func);
 
-        if (lstat(path, &info) < 0)
+        if (fstatat(fd, strempty(path), &st,
+                    (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
                 return -errno;
 
-        return !!S_ISLNK(info.st_mode);
+        r = verify_func(&st);
+        return verify ? r : r >= 0;
 }
-#endif /* NM_IGNORED */
 
-int is_dir_full(int atfd, const char* path, bool follow) {
-        struct stat st;
-        int r;
+int stat_verify_regular(const struct stat *st) {
+        assert(st);
 
-        assert(atfd >= 0 || atfd == AT_FDCWD);
-        assert(atfd >= 0 || path);
+        /* Checks whether the specified stat() structure refers to a regular file. If not returns an
+         * appropriate error code. */
 
-        if (path)
-                r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW);
-        else
-                r = fstat(atfd, &st);
-        if (r < 0)
-                return -errno;
+        if (S_ISDIR(st->st_mode))
+                return -EISDIR;
+
+        if (S_ISLNK(st->st_mode))
+                return -ELOOP;
+
+        if (!S_ISREG(st->st_mode))
+                return -EBADFD;
 
-        return !!S_ISDIR(st.st_mode);
+        return 0;
 }
 
-#if 0 /* NM_IGNORED */
-int is_device_node(const char *path) {
-        struct stat info;
+int verify_regular_at(int fd, const char *path, bool follow) {
+        return verify_stat_at(fd, path, follow, stat_verify_regular, true);
+}
 
-        assert(path);
+int fd_verify_regular(int fd) {
+        assert(fd >= 0);
+        return verify_regular_at(fd, NULL, false);
+}
 
-        if (lstat(path, &info) < 0)
-                return -errno;
+int stat_verify_directory(const struct stat *st) {
+        assert(st);
+
+        if (S_ISLNK(st->st_mode))
+                return -ELOOP;
+
+        if (!S_ISDIR(st->st_mode))
+                return -ENOTDIR;
+
+        return 0;
+}
+
+int fd_verify_directory(int fd) {
+        assert(fd >= 0);
+        return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
+}
+
+int is_dir_at(int fd, const char *path, bool follow) {
+        return verify_stat_at(fd, path, follow, stat_verify_directory, false);
+}
+
+int is_dir(const char *path, bool follow) {
+        assert(!isempty(path));
+        return is_dir_at(AT_FDCWD, path, follow);
+}
+
+int stat_verify_symlink(const struct stat *st) {
+        assert(st);
+
+        if (S_ISDIR(st->st_mode))
+                return -EISDIR;
+
+        if (!S_ISLNK(st->st_mode))
+                return -ENOLINK;
+
+        return 0;
+}
+
+int is_symlink(const char *path) {
+        assert(!isempty(path));
+        return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
+}
+
+int stat_verify_linked(const struct stat *st) {
+        assert(st);
+
+        if (st->st_nlink <= 0)
+                return -EIDRM; /* recognizable error. */
+
+        return 0;
+}
+
+int fd_verify_linked(int fd) {
+        assert(fd >= 0);
+        return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
+}
+
+int stat_verify_device_node(const struct stat *st) {
+        assert(st);
+
+        if (S_ISLNK(st->st_mode))
+                return -ELOOP;
+
+        if (S_ISDIR(st->st_mode))
+                return -EISDIR;
+
+        if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
+                return -ENOTTY;
+
+        return 0;
+}
 
-        return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+#if 0 /* NM_IGNORED */
+int is_device_node(const char *path) {
+        assert(!isempty(path));
+        return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
 }
 
 int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
@@ -193,14 +277,12 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
         struct stat a, b;
 
         assert(fda >= 0 || fda == AT_FDCWD);
-        assert(filea);
         assert(fdb >= 0 || fdb == AT_FDCWD);
-        assert(fileb);
 
-        if (fstatat(fda, filea, &a, flags) < 0)
+        if (fstatat(fda, strempty(filea), &a, flags) < 0)
                 return log_debug_errno(errno, "Cannot stat %s: %m", filea);
 
-        if (fstatat(fdb, fileb, &b, flags) < 0)
+        if (fstatat(fdb, strempty(fileb), &b, flags) < 0)
                 return log_debug_errno(errno, "Cannot stat %s: %m", fileb);
 
         return stat_inode_same(&a, &b);
@@ -270,72 +352,6 @@ int path_is_network_fs(const char *path) {
 }
 #endif /* NM_IGNORED */
 
-int stat_verify_regular(const struct stat *st) {
-        assert(st);
-
-        /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
-         * code. */
-
-        if (S_ISDIR(st->st_mode))
-                return -EISDIR;
-
-        if (S_ISLNK(st->st_mode))
-                return -ELOOP;
-
-        if (!S_ISREG(st->st_mode))
-                return -EBADFD;
-
-        return 0;
-}
-
-int fd_verify_regular(int fd) {
-        struct stat st;
-
-        assert(fd >= 0);
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        return stat_verify_regular(&st);
-}
-
-#if 0 /* NM_IGNORED */
-int verify_regular_at(int dir_fd, const char *path, bool follow) {
-        struct stat st;
-
-        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
-        assert(path);
-
-        if (fstatat(dir_fd, path, &st, (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
-                return -errno;
-
-        return stat_verify_regular(&st);
-}
-
-int stat_verify_directory(const struct stat *st) {
-        assert(st);
-
-        if (S_ISLNK(st->st_mode))
-                return -ELOOP;
-
-        if (!S_ISDIR(st->st_mode))
-                return -ENOTDIR;
-
-        return 0;
-}
-
-int fd_verify_directory(int fd) {
-        struct stat st;
-
-        assert(fd >= 0);
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        return stat_verify_directory(&st);
-}
-#endif /* NM_IGNORED */
-
 int proc_mounted(void) {
         int r;
 
@@ -405,21 +421,35 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
                 a->stx_dev_minor == b->stx_dev_minor;
 }
 
+static bool is_statx_fatal_error(int err, int flags) {
+        assert(err < 0);
+
+        /* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
+         * let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
+         * fs access issues, which we should propagate. */
+        if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
+                return false;
+
+        /* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
+         * See statx_generic() in glibc. */
+        if (err != -EINVAL)
+                return true;
+
+        if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
+                return false; /* Unsupported flags are specified. Let's try to use our implementation. */
+
+        return true;
+}
+
 int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
         static bool avoid_statx = false;
         struct stat st;
+        int r;
 
         if (!avoid_statx) {
-                if (statx(dfd, path, flags, mask, sx) < 0) {
-                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
-                                return -errno;
-
-                        /* If statx() is not supported or if we see EPERM (which might indicate seccomp
-                         * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
-                         * since that is likely an indication of fs access issues, which we should
-                         * propagate */
-                } else
-                        return 0;
+                r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
+                if (r >= 0 || is_statx_fatal_error(r, flags))
+                        return r;
 
                 avoid_statx = true;
         }
@@ -468,7 +498,7 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
         assert(ret);
 
-        fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
+        fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
         if (fd < 0)
                 return fd;
 
@@ -477,8 +507,8 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
 
 #if 0 /* NM_IGNORED */
 void inode_hash_func(const struct stat *q, struct siphash *state) {
-        siphash24_compress(&q->st_dev, sizeof(q->st_dev), state);
-        siphash24_compress(&q->st_ino, sizeof(q->st_ino), state);
+        siphash24_compress_typesafe(q->st_dev, state);
+        siphash24_compress_typesafe(q->st_ino, state);
 }
 
 int inode_compare_func(const struct stat *a, const struct stat *b) {
@@ -503,6 +533,8 @@ const char* inode_type_to_string(mode_t m) {
                 return "reg";
         case S_IFDIR:
                 return "dir";
+        case S_IFLNK:
+                return "lnk";
         case S_IFCHR:
                 return "chr";
         case S_IFBLK:
@@ -515,4 +547,26 @@ const char* inode_type_to_string(mode_t m) {
 
         return NULL;
 }
+
+mode_t inode_type_from_string(const char *s) {
+        if (!s)
+                return MODE_INVALID;
+
+        if (streq(s, "reg"))
+                return S_IFREG;
+        if (streq(s, "dir"))
+                return S_IFDIR;
+        if (streq(s, "lnk"))
+                return S_IFLNK;
+        if (streq(s, "chr"))
+                return S_IFCHR;
+        if (streq(s, "blk"))
+                return S_IFBLK;
+        if (streq(s, "fifo"))
+                return S_IFIFO;
+        if (streq(s, "sock"))
+                return S_IFSOCK;
+
+        return MODE_INVALID;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h
index ae0aaf8f..7eb951a7 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -12,15 +12,24 @@
 #include "macro.h"
 #include "missing_stat.h"
 #include "siphash24.h"
+#include "time-util.h"
 
+int stat_verify_regular(const struct stat *st);
+int verify_regular_at(int fd, const char *path, bool follow);
+int fd_verify_regular(int fd);
+
+int stat_verify_directory(const struct stat *st);
+int fd_verify_directory(int fd);
+int is_dir_at(int fd, const char *path, bool follow);
+int is_dir(const char *path, bool follow);
+
+int stat_verify_symlink(const struct stat *st);
 int is_symlink(const char *path);
-int is_dir_full(int atfd, const char *fname, bool follow);
-static inline int is_dir(const char *path, bool follow) {
-        return is_dir_full(AT_FDCWD, path, follow);
-}
-static inline int is_dir_fd(int fd) {
-        return is_dir_full(fd, NULL, false);
-}
+
+int stat_verify_linked(const struct stat *st);
+int fd_verify_linked(int fd);
+
+int stat_verify_device_node(const struct stat *st);
 int is_device_node(const char *path);
 
 int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup);
@@ -71,13 +80,6 @@ int path_is_network_fs(const char *path);
  */
 #define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
 
-int stat_verify_regular(const struct stat *st);
-int fd_verify_regular(int fd);
-int verify_regular_at(int dir_fd, const char *path, bool follow);
-
-int stat_verify_directory(const struct stat *st);
-int fd_verify_directory(int fd);
-
 int proc_mounted(void);
 
 bool stat_inode_same(const struct stat *a, const struct stat *b);
@@ -109,8 +111,18 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
         } var
 #endif
 
+#if 0 /* NM_IGNORED */
+static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
+        return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+static inline nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
+        return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
+}
+#endif /* NM_IGNORED */
+
 void inode_hash_func(const struct stat *q, struct siphash *state);
 int inode_compare_func(const struct stat *a, const struct stat *b);
 extern const struct hash_ops inode_hash_ops;
 
 const char* inode_type_to_string(mode_t m);
+mode_t inode_type_from_string(const char *s);
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index 1afa49bb..59e65918 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -18,6 +18,7 @@
 #include "macro.h"
 #include "memory-util.h"
 #include "memstream-util.h"
+#include "path-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -174,10 +175,15 @@ char *delete_trailing_chars(char *s, const char *bad) {
 }
 #endif /* NM_IGNORED */
 
-char *truncate_nl(char *s) {
+char *truncate_nl_full(char *s, size_t *ret_len) {
+        size_t n;
+
         assert(s);
 
-        s[strcspn(s, NEWLINE)] = 0;
+        n = strcspn(s, NEWLINE);
+        s[n] = '\0';
+        if (ret_len)
+                *ret_len = n;
         return s;
 }
 
@@ -294,6 +300,62 @@ static int write_ellipsis(char *buf, bool unicode) {
         return 3;
 }
 
+static size_t ansi_sequence_length(const char *s, size_t len) {
+        assert(s);
+
+        if (len < 2)
+                return 0;
+
+        if (s[0] != 0x1B)  /* ASCII 27, aka ESC, aka Ctrl-[ */
+                return 0;  /* Not the start of a sequence */
+
+        if (s[1] == 0x5B) { /* [, start of CSI sequence */
+                size_t i = 2;
+
+                if (i == len)
+                        return 0;
+
+                while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
+                        if (++i == len)
+                                return 0;
+                while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
+                        if (++i == len)
+                                return 0;
+                if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
+                        return i + 1;
+                return 0;  /* Bad sequence */
+
+        } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
+                return 2;
+
+        return 0;  /* Bad escape? */
+}
+
+static bool string_has_ansi_sequence(const char *s, size_t len) {
+        const char *t = s;
+
+        while ((t = memchr(s, 0x1B, len - (t - s))))
+                if (ansi_sequence_length(t, len - (t - s)) > 0)
+                        return true;
+        return false;
+}
+
+static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
+        /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
+
+        for (size_t i = length - 2; i > 0; i--) {  /* -2 because at least two bytes are needed */
+                size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
+                if (slen == 0)
+                        continue;
+
+                *ret_where = s + (i - 1);
+                return slen;
+        }
+
+        *ret_where = NULL;
+        return 0;
+}
+
 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
         size_t x, need_space, suffix_len;
         char *t;
@@ -353,7 +415,6 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
         size_t x, k, len, len2;
         const char *i, *j;
-        char *e;
         int r;
 
         /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
@@ -377,73 +438,116 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
         if (new_length == 0)
                 return strdup("");
 
-        /* If no multibyte characters use ascii_ellipsize_mem for speed */
-        if (ascii_is_valid_n(s, old_length))
+        bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
+
+        /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
+        if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
                 return ascii_ellipsize_mem(s, old_length, new_length, percent);
 
-        x = ((new_length - 1) * percent) / 100;
+        x = (new_length - 1) * percent / 100;
         assert(x <= new_length - 1);
 
         k = 0;
-        for (i = s; i < s + old_length; i = utf8_next_char(i)) {
-                char32_t c;
-                int w;
+        for (i = s; i < s + old_length; ) {
+                size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
+                if (slen > 0) {
+                        i += slen;
+                        continue;  /* ANSI sequences don't take up any space in output */
+                }
 
+                char32_t c;
                 r = utf8_encoded_to_unichar(i, &c);
                 if (r < 0)
                         return NULL;
 
-                w = unichar_iswide(c) ? 2 : 1;
-                if (k + w <= x)
-                        k += w;
-                else
+                int w = unichar_iswide(c) ? 2 : 1;
+                if (k + w > x)
                         break;
+
+                k += w;
+                i += r;
         }
 
-        for (j = s + old_length; j > i; ) {
+        const char *ansi_start = s + old_length;
+        size_t ansi_len = 0;
+
+        for (const char *t = j = s + old_length; t > i && k < new_length; ) {
                 char32_t c;
                 int w;
-                const char *jj;
+                const char *tt;
 
-                jj = utf8_prev_char(j);
-                r = utf8_encoded_to_unichar(jj, &c);
+                if (has_ansi_seq && ansi_start >= t)
+                        /* Figure out the previous ANSI sequence, if any */
+                        ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
+
+                /* If the sequence extends all the way to the current position, skip it. */
+                if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
+                        t = ansi_start;
+                        continue;
+                }
+
+                tt = utf8_prev_char(t);
+                r = utf8_encoded_to_unichar(tt, &c);
                 if (r < 0)
                         return NULL;
 
                 w = unichar_iswide(c) ? 2 : 1;
-                if (k + w <= new_length) {
-                        k += w;
-                        j = jj;
-                } else
+                if (k + w > new_length)
                         break;
+
+                k += w;
+                j = t = tt;  /* j should always point to the first "real" character */
         }
-        assert(i <= j);
 
-        /* we don't actually need to ellipsize */
-        if (i == j)
+        /* We don't actually need to ellipsize */
+        if (i >= j)
                 return memdup_suffix0(s, old_length);
 
-        /* make space for ellipsis, if possible */
-        if (j < s + old_length)
-                j = utf8_next_char(j);
-        else if (i > s)
-                i = utf8_prev_char(i);
+        if (k >= new_length) {
+                /* Make space for ellipsis, if required and possible. We know that the edge character is not
+                 * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
+                 * was wide, we don't need to make space. */
+                if (j < s + old_length)
+                        j = utf8_next_char(j);
+                else if (i > s)
+                        i = utf8_prev_char(i);
+        }
 
         len = i - s;
         len2 = s + old_length - j;
-        e = new(char, len + 3 + len2 + 1);
+
+        /* If we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
+         * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
+         * short anyway. */
+        size_t alloc_len = has_ansi_seq ? old_length + 3 + 1 : len + 3 + len2 + 1;
+
+        char *e = new(char, alloc_len);
         if (!e)
                 return NULL;
 
         /*
-        printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+        printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n",
                old_length, new_length, x, len, len2, k);
         */
 
-        memcpy(e, s, len);
+        memcpy_safe(e, s, len);
         write_ellipsis(e + len, true);
-        memcpy(e + len + 3, j, len2);
-        *(e + len + 3 + len2) = '\0';
+
+        char *dst = e + len + 3;
+
+        if (has_ansi_seq)
+                /* Copy over any ANSI sequences in full */
+                for (const char *p = s + len; p < j; ) {
+                        size_t slen = ansi_sequence_length(p, j - p);
+                        if (slen > 0) {
+                                dst = mempcpy(dst, p, slen);
+                                p += slen;
+                        } else
+                                p = utf8_next_char(p);
+                }
+
+        memcpy_safe(dst, j, len2);
+        dst[len2] = '\0';
 
         return e;
 }
@@ -521,6 +625,9 @@ char *cellescape(char *buf, size_t len, const char *s) {
 char* strshorten(char *s, size_t l) {
         assert(s);
 
+        if (l >= SIZE_MAX-1) /* Would not change anything */
+                return s;
+
         if (strnlen(s, l+1) > l)
                 s[l] = 0;
 
@@ -528,14 +635,23 @@ char* strshorten(char *s, size_t l) {
 }
 
 int strgrowpad0(char **s, size_t l) {
+        size_t sz;
+
         assert(s);
 
+        if (*s) {
+                sz = strlen(*s) + 1;
+                if (sz >= l) /* never shrink */
+                        return 0;
+        } else
+                sz = 0;
+
         char *q = realloc(*s, l);
         if (!q)
                 return -ENOMEM;
+
         *s = q;
 
-        size_t sz = strlen(*s);
         memzero(*s + sz, l - sz);
         return 0;
 }
@@ -893,6 +1009,33 @@ oom:
         return -ENOMEM;
 }
 
+char *strextendn(char **x, const char *s, size_t l) {
+        assert(x);
+        assert(s || l == 0);
+
+        if (l == SIZE_MAX)
+                l = strlen_ptr(s);
+        else if (l > 0)
+                l = strnlen(s, l); /* ignore trailing noise */
+
+        if (l > 0 || !*x) {
+                size_t q;
+                char *m;
+
+                q = strlen_ptr(*x);
+                m = realloc(*x, q + l + 1);
+                if (!m)
+                        return NULL;
+
+                memcpy_safe(m + q, s, l);
+                m[q + l] = 0;
+
+                *x = m;
+        }
+
+        return *x;
+}
+
 char *strrep(const char *s, unsigned n) {
         char *r, *p;
         size_t l;
@@ -1179,6 +1322,7 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
 
         return in_charset(s1, ok) && in_charset(s2, ok);
 }
+#endif /* NM_IGNORED */
 
 char *string_replace_char(char *str, char old_char, char new_char) {
         assert(str);
@@ -1250,6 +1394,7 @@ size_t strspn_from_end(const char *str, const char *accept) {
         return n;
 }
 
+#if 0 /* NM_IGNORED */
 char *strdupspn(const char *a, const char *accept) {
         if (isempty(a) || isempty(accept))
                 return strdup("");
@@ -1290,14 +1435,109 @@ char *find_line_startswith(const char *haystack, const char *needle) {
 }
 #endif /* NM_IGNORED */
 
-char *startswith_strv(const char *string, char **strv) {
-        char *found = NULL;
+bool version_is_valid(const char *s) {
+        if (isempty(s))
+                return false;
+
+        if (!filename_part_is_valid(s))
+                return false;
 
-        STRV_FOREACH(i, strv) {
-                found = startswith(string, *i);
-                if (found)
-                        break;
+        /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
+        if (!in_charset(s, ALPHANUMERICAL ".,_-+"))
+                return false;
+
+        return true;
+}
+
+bool version_is_valid_versionspec(const char *s) {
+        if (!filename_part_is_valid(s))
+                return false;
+
+        if (!in_charset(s, ALPHANUMERICAL "-.~^"))
+                return false;
+
+        return true;
+}
+
+ssize_t strlevenshtein(const char *x, const char *y) {
+        _cleanup_free_ size_t *t0 = NULL, *t1 = NULL, *t2 = NULL;
+        size_t xl, yl;
+
+        /* This is inspired from the Linux kernel's Levenshtein implementation */
+
+        if (streq_ptr(x, y))
+                return 0;
+
+        xl = strlen_ptr(x);
+        if (xl > SSIZE_MAX)
+                return -E2BIG;
+
+        yl = strlen_ptr(y);
+        if (yl > SSIZE_MAX)
+                return -E2BIG;
+
+        if (isempty(x))
+                return yl;
+        if (isempty(y))
+                return xl;
+
+        t0 = new0(size_t, yl + 1);
+        if (!t0)
+                return -ENOMEM;
+        t1 = new0(size_t, yl + 1);
+        if (!t1)
+                return -ENOMEM;
+        t2 = new0(size_t, yl + 1);
+        if (!t2)
+                return -ENOMEM;
+
+        for (size_t i = 0; i <= yl; i++)
+                t1[i] = i;
+
+        for (size_t i = 0; i < xl; i++) {
+                t2[0] = i + 1;
+
+                for (size_t j = 0; j < yl; j++) {
+                        /* Substitution */
+                        t2[j+1] = t1[j] + (x[i] != y[j]);
+
+                        /* Swap */
+                        if (i > 0 && j > 0 && x[i-1] == y[j] && x[i] == y[j-1] && t2[j+1] > t0[j-1] + 1)
+                                t2[j+1] = t0[j-1] + 1;
+
+                        /* Deletion */
+                        if (t2[j+1] > t1[j+1] + 1)
+                                t2[j+1] = t1[j+1] + 1;
+
+                        /* Insertion */
+                        if (t2[j+1] > t2[j] + 1)
+                                t2[j+1] = t2[j] + 1;
+                }
+
+                size_t *dummy = t0;
+                t0 = t1;
+                t1 = t2;
+                t2 = dummy;
         }
 
-        return found;
+        return t1[yl];
+}
+
+char *strrstr(const char *haystack, const char *needle) {
+        /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
+
+        if (!haystack || !needle)
+                return NULL;
+
+        /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
+         * last char, not before. */
+        if (*needle == 0)
+                return strchr(haystack, 0);
+
+        for (const char *p = strstr(haystack, needle), *q; p; p = q) {
+                q = strstr(p + 1, needle);
+                if (!q)
+                        return (char *) p;
+        }
+        return NULL;
 }
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 4430910e..e162765a 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -22,6 +22,9 @@
 #define ALPHANUMERICAL      LETTERS DIGITS
 #define HEXDIGITS           DIGITS "abcdefABCDEF"
 #define LOWERCASE_HEXDIGITS DIGITS "abcdef"
+#define URI_RESERVED        ":/?#[]@!$&'()*+;="         /* [RFC3986] */
+#define URI_UNRESERVED      ALPHANUMERICAL "-._~"       /* [RFC3986] */
+#define URI_VALID           URI_RESERVED URI_UNRESERVED /* [RFC3986] */
 
 static inline char* strstr_ptr(const char *haystack, const char *needle) {
         if (!haystack || !needle)
@@ -65,6 +68,10 @@ static inline const char* enable_disable(bool b) {
         return b ? "enable" : "disable";
 }
 
+static inline const char* enabled_disabled(bool b) {
+        return b ? "enabled" : "disabled";
+}
+
 /* This macro's return pointer will have the "const" qualifier set or unset the same way as the input
  * pointer. */
 #define empty_to_null(p)                                \
@@ -121,7 +128,10 @@ char *strjoin_real(const char *x, ...) _sentinel_;
 char *strstrip(char *s);
 char *delete_chars(char *s, const char *bad);
 char *delete_trailing_chars(char *s, const char *bad);
-char *truncate_nl(char *s);
+char *truncate_nl_full(char *s, size_t *ret_len);
+static inline char *truncate_nl(char *s) {
+        return truncate_nl_full(s, NULL);
+}
 
 static inline char *skip_leading_chars(const char *s, const char *bad) {
         if (!s)
@@ -183,11 +193,24 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) _s
 #define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL)
 #define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL)
 
+char *strextendn(char **x, const char *s, size_t l);
+
 int strextendf_with_separator(char **x, const char *separator, const char *format, ...) _printf_(3,4);
 #define strextendf(x, ...) strextendf_with_separator(x, NULL, __VA_ARGS__)
 
 char *strrep(const char *s, unsigned n);
 
+#define strrepa(s, n)                                           \
+        ({                                                      \
+                char *_d_, *_p_;                                \
+                size_t _len_ = strlen(s) * n;                   \
+                _p_ = _d_ = newa(char, _len_ + 1);              \
+                for (unsigned _i_ = 0; _i_ < n; _i_++)          \
+                        _p_ = stpcpy(_p_, s);                   \
+                *_p_ = 0;                                       \
+                _d_;                                            \
+        })
+
 int split_pair(const char *s, const char *sep, char **l, char **r);
 
 int free_and_strdup(char **p, const char *s);
@@ -268,7 +291,31 @@ char *strdupcspn(const char *a, const char *reject);
 
 char *find_line_startswith(const char *haystack, const char *needle);
 
-char *startswith_strv(const char *string, char **strv);
+bool version_is_valid(const char *s);
+
+bool version_is_valid_versionspec(const char *s);
+
+ssize_t strlevenshtein(const char *x, const char *y);
+
+static inline int strdup_or_null(const char *s, char **ret) {
+        char *c;
+
+        assert(ret);
+
+        /* This is a lot like strdup(), but is happy with NULL strings, and does not treat that as error, but
+         * copies the NULL value. */
+
+        if (!s) {
+                *ret = NULL;
+                return 0;
+        }
+
+        c = strdup(s);
+        if (!c)
+                return -ENOMEM;
+
+        *ret = c;
+        return 1;
+}
 
-#define STARTSWITH_SET(p, ...)                                  \
-        startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+char *strrstr(const char *haystack, const char *needle);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index 9ad53307..ed02b481 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -90,6 +90,15 @@ char** strv_free_erase(char **l) {
         return mfree(l);
 }
 
+void strv_free_many(char ***strvs, size_t n) {
+        assert(strvs || n == 0);
+
+        FOREACH_ARRAY (i, strvs, n)
+                strv_free(*i);
+
+        free(strvs);
+}
+
 char** strv_copy_n(char * const *l, size_t m) {
         _cleanup_strv_free_ char **result = NULL;
         char **k;
@@ -116,6 +125,22 @@ char** strv_copy_n(char * const *l, size_t m) {
         return TAKE_PTR(result);
 }
 
+int strv_copy_unless_empty(char * const *l, char ***ret) {
+        assert(ret);
+
+        if (strv_isempty(l)) {
+                *ret = NULL;
+                return 0;
+        }
+
+        char **copy = strv_copy(l);
+        if (!copy)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(copy);
+        return 1;
+}
+
 size_t strv_length(char * const *l) {
         size_t n = 0;
 
@@ -214,9 +239,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
         return (int) i;
 
 rollback:
-        for (size_t j = 0; j < i; j++)
-                free(t[p + j]);
-
+        free_many_charp(t + p, i);
         t[p] = NULL;
         return -ENOMEM;
 }
@@ -340,7 +363,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
 
                 const char *p = tuple;
                 r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
-                                       &first, &second, NULL);
+                                       &first, &second);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -488,29 +511,31 @@ int strv_insert(char ***l, size_t position, char *value) {
         char **c;
         size_t n, m;
 
+        assert(l);
+
         if (!value)
                 return 0;
 
         n = strv_length(*l);
         position = MIN(position, n);
 
-        /* increase and check for overflow */
-        m = n + 2;
-        if (m < n)
+        /* check for overflow and increase*/
+        if (n > SIZE_MAX - 2)
                 return -ENOMEM;
+        m = n + 2;
 
-        c = new(char*, m);
+        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m), sizeof(char*));
         if (!c)
                 return -ENOMEM;
 
-        for (size_t i = 0; i < position; i++)
-                c[i] = (*l)[i];
+        if (n > position)
+                memmove(c + position + 1, c + position, (n - position) * sizeof(char*));
+
         c[position] = value;
-        for (size_t i = position; i < n; i++)
-                c[i+1] = (*l)[i];
-        c[n+1] = NULL;
+        c[n + 1] = NULL;
 
-        return free_and_replace(*l, c);
+        *l = c;
+        return 0;
 }
 
 int strv_consume_with_size(char ***l, size_t *n, char *value) {
@@ -571,39 +596,63 @@ int strv_extend_with_size(char ***l, size_t *n, const char *value) {
         return strv_consume_with_size(l, n, v);
 }
 
-int strv_extend_front(char ***l, const char *value) {
+int strv_extend_many_internal(char ***l, const char *value, ...) {
+        va_list ap;
         size_t n, m;
-        char *v, **c;
+        int r;
 
         assert(l);
 
-        /* Like strv_extend(), but prepends rather than appends the new entry */
+        m = n = strv_length(*l);
 
-        if (!value)
-                return 0;
+        r = 0;
+        va_start(ap, value);
+        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+                if (!s)
+                        continue;
 
-        n = strv_length(*l);
+                if (m > SIZE_MAX-1) { /* overflow */
+                        r = -ENOMEM;
+                        break;
+                }
+                m++;
+        }
+        va_end(ap);
 
-        /* Increase and overflow check. */
-        m = n + 2;
-        if (m < n)
+        if (r < 0)
+                return r;
+        if (m > SIZE_MAX-1)
                 return -ENOMEM;
 
-        v = strdup(value);
-        if (!v)
+        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(m+1), sizeof(char*));
+        if (!c)
                 return -ENOMEM;
+        *l = c;
 
-        c = reallocarray(*l, m, sizeof(char*));
-        if (!c) {
-                free(v);
-                return -ENOMEM;
+        r = 0;
+        size_t i = n;
+        va_start(ap, value);
+        for (const char *s = value; s != POINTER_MAX; s = va_arg(ap, const char*)) {
+                if (!s)
+                        continue;
+
+                c[i] = strdup(s);
+                if (!c[i]) {
+                        r = -ENOMEM;
+                        break;
+                }
+                i++;
         }
+        va_end(ap);
 
-        memmove(c+1, c, n * sizeof(char*));
-        c[0] = v;
-        c[n+1] = NULL;
+        if (r < 0) {
+                /* rollback on error */
+                for (size_t j = n; j < i; j++)
+                        c[j] = mfree(c[j]);
+                return r;
+        }
 
-        *l = c;
+        c[i] = NULL;
         return 0;
 }
 
@@ -707,6 +756,26 @@ int strv_extendf(char ***l, const char *format, ...) {
         return strv_consume(l, x);
 }
 
+char* startswith_strv(const char *s, char * const *l) {
+        STRV_FOREACH(i, l) {
+                char *found = startswith(s, *i);
+                if (found)
+                        return found;
+        }
+
+        return NULL;
+}
+
+char* endswith_strv(const char *s, char * const *l) {
+        STRV_FOREACH(i, l) {
+                char *found = endswith(s, *i);
+                if (found)
+                        return found;
+        }
+
+        return NULL;
+}
+
 char** strv_reverse(char **l) {
         size_t n;
 
@@ -835,13 +904,15 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
         bool b = false;
         int r;
 
+        assert(f);
+
         /* Like fputs(), but for strv, and with a less stupid argument order */
 
         if (!space)
                 space = &b;
 
         STRV_FOREACH(s, l) {
-                r = fputs_with_space(f, *s, separator, space);
+                r = fputs_with_separator(f, *s, separator, space);
                 if (r < 0)
                         return r;
         }
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 544d46a3..91337b92 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -32,10 +32,14 @@ char** strv_free_erase(char **l);
 DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
 #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
 
+void strv_free_many(char ***strvs, size_t n);
+
 char** strv_copy_n(char * const *l, size_t n);
 static inline char** strv_copy(char * const *l) {
         return strv_copy_n(l, SIZE_MAX);
 }
+int strv_copy_unless_empty(char * const *l, char ***ret);
+
 size_t strv_length(char * const *l) _pure_;
 
 int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@@ -51,8 +55,10 @@ static inline int strv_extend(char ***l, const char *value) {
         return strv_extend_with_size(l, NULL, value);
 }
 
+int strv_extend_many_internal(char ***l, const char *value, ...);
+#define strv_extend_many(l, ...) strv_extend_many_internal(l, __VA_ARGS__, POINTER_MAX)
+
 int strv_extendf(char ***l, const char *format, ...) _printf_(2,3);
-int strv_extend_front(char ***l, const char *value);
 
 int strv_push_with_size(char ***l, size_t *n, char *value);
 static inline int strv_push(char ***l, char *value) {
@@ -157,6 +163,16 @@ static inline void strv_print(char * const *l) {
         strv_print_full(l, NULL);
 }
 
+char* startswith_strv(const char *s, char * const *l);
+
+#define STARTSWITH_SET(p, ...)                                  \
+        startswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
+char* endswith_strv(const char *s, char * const *l);
+
+#define ENDSWITH_SET(p, ...)                                    \
+        endswith_strv(p, STRV_MAKE(__VA_ARGS__))
+
 #define strv_from_stdarg_alloca(first)                          \
         ({                                                      \
                 char **_l;                                      \
@@ -200,18 +216,6 @@ static inline void strv_print(char * const *l) {
                 _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
         })
 
-#define ENDSWITH_SET(p, ...)                                    \
-        ({                                                      \
-                const char *_p = (p);                           \
-                char *_found = NULL;                            \
-                STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
-                        _found = endswith(_p, *_i);             \
-                        if (_found)                             \
-                                break;                          \
-                }                                               \
-                _found;                                         \
-        })
-
 #define _FOREACH_STRING(uniq, x, y, ...)                                \
         for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \
              x;                                                         \
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 092912b2..47f5bb5a 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -66,7 +66,7 @@ nsec_t now_nsec(clockid_t clock_id) {
         return timespec_load_nsec(&ts);
 }
 
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {
         assert(ts);
 
         ts->realtime = now(CLOCK_REALTIME);
@@ -75,7 +75,7 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
         return ts;
 }
 
-triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
+triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
         assert(ts);
 
         ts->realtime = now(CLOCK_REALTIME);
@@ -158,6 +158,25 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
         return ts;
 }
 
+triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) {
+        usec_t nowb;
+
+        assert(ts);
+
+        if (u == USEC_INFINITY) {
+                ts->realtime = ts->monotonic = ts->boottime = u;
+                return ts;
+        }
+
+        nowb = now(CLOCK_BOOTTIME);
+
+        ts->boottime = u;
+        ts->monotonic = map_clock_usec_internal(u, nowb, now(CLOCK_MONOTONIC));
+        ts->realtime = map_clock_usec_internal(u, nowb, now(CLOCK_REALTIME));
+
+        return ts;
+}
+
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
         assert(ts);
 
@@ -330,7 +349,7 @@ char *format_timestamp_style(
                 if (l < (size_t) (1 + 1 + 1))
                         return NULL; /* not enough space for even the shortest of forms */
 
-                return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC);  /* round down µs → s */
+                return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC);  /* round down μs → s */
         }
 
         utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
@@ -618,7 +637,7 @@ char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
 #if 0 /* NM_IGNORED */
 static int parse_timestamp_impl(
                 const char *t,
-                size_t tz_offset,
+                size_t max_len,
                 bool utc,
                 int isdst,
                 long gmtoff,
@@ -655,8 +674,12 @@ static int parse_timestamp_impl(
 
         /* Allowed syntaxes:
          *
-         *   2012-09-22 16:34:22
+         *   2012-09-22 16:34:22.1[2[3[4[5[6]]]]]
+         *   2012-09-22 16:34:22  (µsec will be set to 0)
          *   2012-09-22 16:34     (seconds will be set to 0)
+         *   2012-09-22T16:34:22.1[2[3[4[5[6]]]]]
+         *   2012-09-22T16:34:22  (µsec will be set to 0)
+         *   2012-09-22T16:34     (seconds will be set to 0)
          *   2012-09-22           (time will be set to 00:00:00)
          *   16:34:22             (date will be set to today)
          *   16:34                (date will be set to today, seconds to 0)
@@ -670,17 +693,26 @@ static int parse_timestamp_impl(
          *
          * Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
          * E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
+         *
+         * A simplified strptime-spelled RFC3339 ABNF looks like
+         *   "%Y-%m-%d" "T" "%H" ":" "%M" ":" "%S" [".%N"] ("Z" / (("+" / "-") "%H:%M"))
+         * We additionally allow no seconds and inherited timezone
+         * for symmetry with our other syntaxes and improved interactive usability:
+         *   "%Y-%m-%d" "T" "%H" ":" "%M" ":" ["%S" [".%N"]] ["Z" / (("+" / "-") "%H:%M")]
+         * RFC3339 defines time-secfrac to as "." 1*DIGIT, but we limit to 6 digits,
+         * since we're limited to 1µs resolution.
+         * We also accept "Sat 2012-09-22T16:34:22", RFC3339 warns against it.
          */
 
         assert(t);
 
-        if (tz_offset != SIZE_MAX) {
+        if (max_len != SIZE_MAX) {
                 /* If the input string contains timezone, then cut it here. */
 
-                if (tz_offset <= 1) /* timezone must be after a space. */
+                if (max_len == 0) /* Can't be the only field */
                         return -EINVAL;
 
-                t_alloc = strndup(t, tz_offset - 1);
+                t_alloc = strndup(t, max_len);
                 if (!t_alloc)
                         return -ENOMEM;
 
@@ -792,6 +824,7 @@ static int parse_timestamp_impl(
                         goto from_tm;
         }
 
+        /* Our "canonical" RFC3339 syntax variant */
         tm = copy;
         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
         if (k) {
@@ -801,6 +834,16 @@ static int parse_timestamp_impl(
                         goto from_tm;
         }
 
+        /* RFC3339 syntax */
+        tm = copy;
+        k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm);
+        if (k) {
+                if (*k == '.')
+                        goto parse_usec;
+                else if (*k == 0)
+                        goto from_tm;
+        }
+
         /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
         tm = copy;
         k = strptime(t, "%b %d %H:%M:%S", &tm);
@@ -818,6 +861,7 @@ static int parse_timestamp_impl(
                 goto from_tm;
         }
 
+        /* Our "canonical" RFC3339 syntax variant without seconds */
         tm = copy;
         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
         if (k && *k == 0) {
@@ -825,6 +869,14 @@ static int parse_timestamp_impl(
                 goto from_tm;
         }
 
+        /* RFC3339 syntax without seconds */
+        tm = copy;
+        k = strptime(t, "%Y-%m-%dT%H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto from_tm;
+        }
+
         tm = copy;
         k = strptime(t, "%y-%m-%d", &tm);
         if (k && *k == 0) {
@@ -927,13 +979,13 @@ static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool v
                         continue;
 
                 /* The specified timezone matches tzname[] of the local timezone. */
-                return parse_timestamp_impl(t, tz_offset, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
+                return parse_timestamp_impl(t, tz_offset - 1, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
         }
 
         /* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
          * and parse the remaining string as a local time. If we know that the last word is not a timezone,
          * then assume that it is a part of the time and try to parse the whole string as a local time. */
-        return parse_timestamp_impl(t, valid_tz ? tz_offset : SIZE_MAX,
+        return parse_timestamp_impl(t, valid_tz ? tz_offset - 1 : SIZE_MAX,
                                     /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
 }
 
@@ -945,40 +997,50 @@ typedef struct ParseTimestampResult {
 int parse_timestamp(const char *t, usec_t *ret) {
         ParseTimestampResult *shared, tmp;
         const char *k, *tz, *current_tz;
-        size_t tz_offset;
+        size_t max_len, t_len;
         struct tm tm;
         int r;
 
         assert(t);
 
+        t_len = strlen(t);
+        if (t_len > 2 && t[t_len - 1] == 'Z' && t[t_len - 2] != ' ')  /* RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */
+                return parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+
+        if (t_len > 7 && IN_SET(t[t_len - 6], '+', '-') && t[t_len - 7] != ' ') {  /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */
+                k = strptime(&t[t_len - 6], "%z", &tm);
+                if (k && *k == '\0')
+                        return parse_timestamp_impl(t, t_len - 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
+        }
+
         tz = strrchr(t, ' ');
         if (!tz)
-                return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+                return parse_timestamp_impl(t, /* max_len = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
 
+        max_len = tz - t;
         tz++;
-        tz_offset = tz - t;
 
         /* Shortcut, parse the string as UTC. */
         if (streq(tz, "UTC"))
-                return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
+                return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
 
         /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
          * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
          * tzname[] may be in the same format. */
         k = strptime(tz, "%z", &tm);
         if (k && *k == '\0')
-                return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
+                return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
 
         /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
          * tzname[] of the local timezone, e.g. JST or CEST. */
         if (!timezone_is_valid(tz, LOG_DEBUG))
-                return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ false, ret);
+                return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ false, ret);
 
         /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
          * the process. */
         current_tz = getenv("TZ");
         if (current_tz && *current_tz == ':' && streq(current_tz + 1, tz))
-                return parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, ret);
+                return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, ret);
 
         /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
          * the specified timezone in the child process. */
@@ -987,7 +1049,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
         if (shared == MAP_FAILED)
                 return negative_errno();
 
-        r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+        r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
         if (r < 0) {
                 (void) munmap(shared, sizeof *shared);
                 return r;
@@ -1003,7 +1065,7 @@ int parse_timestamp(const char *t, usec_t *ret) {
                         _exit(EXIT_FAILURE);
                 }
 
-                shared->return_value = parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, &shared->usec);
+                shared->return_value = parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, &shared->usec);
 
                 _exit(EXIT_SUCCESS);
         }
@@ -1051,7 +1113,8 @@ static const char* extract_multiplier(const char *p, usec_t *ret) {
                 { "y",       USEC_PER_YEAR   },
                 { "usec",    1ULL            },
                 { "us",      1ULL            },
-                { "µs",      1ULL            },
+                { "μs",      1ULL            }, /* U+03bc (aka GREEK SMALL LETTER MU) */
+                { "µs",      1ULL            }, /* U+b5 (aka MICRO SIGN) */
         };
 
         assert(p);
@@ -1229,7 +1292,8 @@ static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
                 { "y",       NSEC_PER_YEAR   },
                 { "usec",    NSEC_PER_USEC   },
                 { "us",      NSEC_PER_USEC   },
-                { "µs",      NSEC_PER_USEC   },
+                { "μs",      NSEC_PER_USEC   }, /* U+03bc (aka GREEK LETTER MU) */
+                { "µs",      NSEC_PER_USEC   }, /* U+b5 (aka MICRO SIGN) */
                 { "nsec",    1ULL            },
                 { "ns",      1ULL            },
                 { "",        1ULL            }, /* default is nsec */
@@ -1370,7 +1434,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
 
                 /* Line format is:
                  * 'country codes' 'coordinates' 'timezone' 'comments' */
-                r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
+                r = extract_many_words(&p, NULL, 0, &cc, &co, &tz);
                 if (r < 0)
                         continue;
 
@@ -1415,7 +1479,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
                  * Link line format is:
                  * 'Link' 'target' 'alias'
                  * See 'man zic' for more detail. */
-                r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
+                r = extract_many_words(&p, NULL, 0, &type, &f1, &f2);
                 if (r < 0)
                         continue;
 
@@ -1458,7 +1522,7 @@ int get_timezones(char ***ret) {
         /* Always include UTC */
         r = strv_extend(&zones, "UTC");
         if (r < 0)
-                return -ENOMEM;
+                return r;
 
         strv_sort(zones);
         strv_uniq(zones);
@@ -1515,7 +1579,7 @@ int verify_timezone(const char *name, int log_level) {
 
         r = fd_verify_regular(fd);
         if (r < 0)
-                return log_full_errno(log_level, r, "Timezone file '%s' is not  a regular file: %m", t);
+                return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
 
         r = loop_read_exact(fd, buf, 4, false);
         if (r < 0)
@@ -1666,13 +1730,13 @@ int time_change_fd(void) {
         if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
                 return TAKE_FD(fd);
 
-        /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support
-         * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
+        /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
+         * 64-bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
          * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
          * of a black magic thing though, but what can we do?
          *
-         * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t
-         * but where "long" is shorter than 64bit, i.e. 32bit archs.
+         * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
+         * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
          *
          * See: https://github.com/systemd/systemd/issues/14362 */
 
@@ -1708,9 +1772,9 @@ TimestampStyle timestamp_style_from_string(const char *s) {
         t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
         if (t >= 0)
                 return t;
-        if (streq_ptr(s, "µs"))
+        if (STRPTR_IN_SET(s, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
                 return TIMESTAMP_US;
-        if (streq_ptr(s, "µs+utc"))
+        if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
                 return TIMESTAMP_US_UTC;
         return t;
 }
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index b49137d5..29373477 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -79,13 +79,14 @@ nsec_t now_nsec(clockid_t clock);
 
 usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
 
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp* dual_timestamp_now(dual_timestamp *ts);
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u);
 
-triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
+triple_timestamp* triple_timestamp_now(triple_timestamp *ts);
 triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
+triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u);
 
 #define DUAL_TIMESTAMP_HAS_CLOCK(clock)                               \
         IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
@@ -211,10 +212,24 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
         return usec_sub_unsigned(timestamp, (usec_t) delta);
 }
 
+static inline int usleep_safe(usec_t usec) {
+        /* usleep() takes useconds_t that is (typically?) uint32_t. Also, usleep() may only support the
+         * range [0, 1000000]. See usleep(3). Let's override usleep() with clock_nanosleep().
+         *
+         * ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
+         *    CLOCK_MONOTONIC! */
+
+        if (usec == 0)
+                return 0;
+
+        // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h.
+        return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0;
+}
+
 /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
  * year territory. However, since we want to stay away from this in all timezones we take one day off. */
 #define USEC_TIMESTAMP_FORMATTABLE_MAX_64BIT ((usec_t) 253402214399000000) /* Thu 9999-12-30 23:59:59 UTC */
-/* With a 32bit time_t we can't go beyond 2038...
+/* With a 32-bit time_t we can't go beyond 2038...
  * We parse timestamp with RFC-822/ISO 8601 (e.g. +06, or -03:00) as UTC, hence the upper bound must be off
  * by USEC_PER_DAY. See parse_timestamp() for more details. */
 #define USEC_TIMESTAMP_FORMATTABLE_MAX_32BIT (((usec_t) INT32_MAX) * USEC_PER_SEC - USEC_PER_DAY)
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.h b/src/libnm-systemd-shared/src/basic/tmpfile-util.h
index 50904eca..8c917c06 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.h
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.h
@@ -29,7 +29,6 @@ static inline int open_tmpfile_linkable(const char *target, int flags, char **re
 }
 int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
 
-
 typedef enum LinkTmpfileFlags {
         LINK_TMPFILE_REPLACE = 1 << 0,
         LINK_TMPFILE_SYNC    = 1 << 1,
diff --git a/src/libnm-systemd-shared/src/basic/umask-util.h b/src/libnm-systemd-shared/src/basic/umask-util.h
index 6f0e1cc2..00417fa3 100644
--- a/src/libnm-systemd-shared/src/basic/umask-util.h
+++ b/src/libnm-systemd-shared/src/basic/umask-util.h
@@ -8,12 +8,12 @@
 #include "macro.h"
 
 static inline void umaskp(mode_t *u) {
-        umask(*u & 0777);
+        umask(*u);
 }
 
 #define _cleanup_umask_ _cleanup_(umaskp)
 
-/* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although
+/* We make use of the fact here that the umask() syscall uses only the lower 9 bits of mode_t, although
  * mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to
  * distinguish the first and the second iteration of the WITH_UMASK() loop, so that we can run the first one,
  * and exit on the second. */
diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h
index 8b829a9a..9d07ef31 100644
--- a/src/libnm-systemd-shared/src/basic/user-util.h
+++ b/src/libnm-systemd-shared/src/basic/user-util.h
@@ -42,8 +42,8 @@ typedef enum UserCredsFlags {
         USER_CREDS_CLEAN         = 1 << 2,  /* try to clean up shell and home fields with invalid data */
 } UserCredsFlags;
 
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell, UserCredsFlags flags);
-int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags);
+int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags);
 
 char* uid_to_name(uid_t uid);
 char* gid_to_name(gid_t gid);
@@ -57,7 +57,10 @@ int getgroups_alloc(gid_t** gids);
 int get_home_dir(char **ret);
 int get_shell(char **ret);
 
-int reset_uid_gid(void);
+int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids);
+static inline int reset_uid_gid(void) {
+        return fully_set_uid_gid(0, 0, NULL, 0);
+}
 
 int take_etc_passwd_lock(const char *root);
 
@@ -69,13 +72,13 @@ int take_etc_passwd_lock(const char *root);
 
 /* If REMOUNT_IDMAPPING_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host
  * root user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in
- * the *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
+ * the *signed* 32-bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
  * definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range
  * we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use
- * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is
+ * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32-bit range) is
  * icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following
- * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID
- * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read
+ * our usual logic of assigning a 16-bit UID range to each container, so that the upper 16-bit of a 32-bit UID
+ * value indicate kind of a "container ID" and the lower 16-bit map directly to the intended user you can read
  * this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */
 #define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1))
 #define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1))
@@ -155,3 +158,9 @@ static inline bool hashed_password_is_locked_or_invalid(const char *password) {
  * Also see https://github.com/systemd/systemd/pull/24680#pullrequestreview-1439464325.
  */
 #define PASSWORD_UNPROVISIONED "!unprovisioned"
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret);
+int getpwnam_malloc(const char *name, struct passwd **ret);
+
+int getgrnam_malloc(const char *name, struct group **ret);
+int getgrgid_malloc(gid_t gid, struct group **ret);
diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c
index c8e39fe4..cf24e82f 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.c
+++ b/src/libnm-systemd-shared/src/basic/utf8.c
@@ -92,7 +92,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
         switch (len) {
         case 1:
                 *ret_unichar = (char32_t)str[0];
-                return 0;
+                return 1;
         case 2:
                 unichar = str[0] & 0x1f;
                 break;
@@ -121,15 +121,14 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
         }
 
         *ret_unichar = unichar;
-
-        return 0;
+        return len;
 }
 
 bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
         assert(str);
 
         for (const char *p = str; length > 0;) {
-                int encoded_len, r;
+                int encoded_len;
                 char32_t val;
 
                 encoded_len = utf8_encoded_valid_unichar(p, length);
@@ -137,8 +136,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
                         return false;
                 assert(encoded_len > 0 && (size_t) encoded_len <= length);
 
-                r = utf8_encoded_to_unichar(p, &val);
-                if (r < 0 ||
+                if (utf8_encoded_to_unichar(p, &val) < 0 ||
                     unichar_is_control(val) ||
                     (!allow_newline && val == '\n'))
                         return false;
@@ -398,11 +396,23 @@ char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
         const uint8_t *f;
         char *r, *t;
 
+        if (length == 0)
+                return new0(char, 1);
+
         assert(s);
 
+        if (length == SIZE_MAX) {
+                length = char16_strlen(s);
+
+                if (length > SIZE_MAX/2)
+                        return NULL; /* overflow */
+
+                length *= 2;
+        }
+
         /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
          * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
-        if (length * 2 < length)
+        if (length > (SIZE_MAX - 1) / 2)
                 return NULL; /* overflow */
 
         r = new(char, length * 2 + 1);
@@ -472,8 +482,17 @@ char16_t *utf8_to_utf16(const char *s, size_t length) {
         char16_t *n, *p;
         int r;
 
+        if (length == 0)
+                return new0(char16_t, 1);
+
         assert(s);
 
+        if (length == SIZE_MAX)
+                length = strlen(s);
+
+        if (length > SIZE_MAX - 1)
+                return NULL; /* overflow */
+
         n = new(char16_t, length + 1);
         if (!n)
                 return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h
index 4a06dd62..962312c5 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.h
+++ b/src/libnm-systemd-shared/src/basic/utf8.h
@@ -38,7 +38,7 @@ size_t utf16_encode_unichar(char16_t *out, char32_t c);
 char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */);
 char16_t *utf8_to_utf16(const char *s, size_t length);
 
-size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */
+size_t char16_strlen(const char16_t *s); /* returns the number of 16-bit words in the string (not bytes!) */
 
 int utf8_encoded_valid_unichar(const char *str, size_t length);
 int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 89b83e7d..00f2a2b9 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -11,6 +11,42 @@
 #include <stddef.h>
 #include <stdint.h>
 
+/* Temporarily disable some warnings */
+#define DISABLE_WARNING_DEPRECATED_DECLARATIONS                         \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+
+#define DISABLE_WARNING_FORMAT_NONLITERAL                               \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
+
+#define DISABLE_WARNING_MISSING_PROTOTYPES                              \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+
+#define DISABLE_WARNING_NONNULL                                         \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wnonnull\"")
+
+#define DISABLE_WARNING_SHADOW                                          \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wshadow\"")
+
+#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES                      \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
+
+#define DISABLE_WARNING_TYPE_LIMITS                                     \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
+
+#define DISABLE_WARNING_ADDRESS                                         \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Waddress\"")
+
+#define REENABLE_WARNING                                                \
+        _Pragma("GCC diagnostic pop")
+
 #define _align_(x) __attribute__((__aligned__(x)))
 #define _alignas_(x) __attribute__((__aligned__(alignof(x))))
 #define _alignptr_ __attribute__((__aligned__(sizeof(void *))))
@@ -79,7 +115,7 @@
         _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
 
         #ifdef NDEBUG
-                #define assert(expr)
+                #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
                 #define assert_not_reached() __builtin_unreachable()
         #else
                 #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
@@ -129,6 +165,10 @@
                 __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
         })
 
+#define U64_KB UINT64_C(1024)
+#define U64_MB (UINT64_C(1024) * U64_KB)
+#define U64_GB (UINT64_C(1024) * U64_MB)
+
 #undef MAX
 #define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
 #define __MAX(aq, a, bq, b)                             \
@@ -224,6 +264,30 @@
                 })
 #endif /* NM_IGNORED */
 
+#define ADD_SAFE(ret, a, b) (!__builtin_add_overflow(a, b, ret))
+#define INC_SAFE(a, b) __INC_SAFE(UNIQ, a, b)
+#define __INC_SAFE(q, a, b)                                     \
+        ({                                                      \
+                const typeof(a) UNIQ_T(A, q) = (a);             \
+                ADD_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b);       \
+        })
+
+#define SUB_SAFE(ret, a, b) (!__builtin_sub_overflow(a, b, ret))
+#define DEC_SAFE(a, b) __DEC_SAFE(UNIQ, a, b)
+#define __DEC_SAFE(q, a, b)                                     \
+        ({                                                      \
+                const typeof(a) UNIQ_T(A, q) = (a);             \
+                SUB_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b);       \
+        })
+
+#define MUL_SAFE(ret, a, b) (!__builtin_mul_overflow(a, b, ret))
+#define MUL_ASSIGN_SAFE(a, b) __MUL_ASSIGN_SAFE(UNIQ, a, b)
+#define __MUL_ASSIGN_SAFE(q, a, b)                              \
+        ({                                                      \
+                const typeof(a) UNIQ_T(A, q) = (a);             \
+                MUL_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b);       \
+        })
+
 #define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
 #define __LESS_BY(aq, a, bq, b)                         \
         ({                                              \
@@ -273,7 +337,7 @@
                 const typeof(y) UNIQ_T(A, q) = (y);                     \
                 const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP((x), UNIQ_T(A, q)); \
                 typeof(x) UNIQ_T(C, q);                                 \
-                __builtin_mul_overflow(UNIQ_T(B, q), UNIQ_T(A, q), &UNIQ_T(C, q)) ? (typeof(x)) -1 : UNIQ_T(C, q); \
+                MUL_SAFE(&UNIQ_T(C, q), UNIQ_T(B, q), UNIQ_T(A, q)) ? UNIQ_T(C, q) : (typeof(x)) -1; \
         })
 #define ROUND_UP(x, y) __ROUND_UP(UNIQ, (x), (y))
 
@@ -355,7 +419,40 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
         if (l > SIZE_MAX - (ali - 1))
                 return SIZE_MAX; /* indicate overflow */
 
-        return ((l + ali - 1) & ~(ali - 1));
+        return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        if (l > UINT64_MAX - (ali - 1))
+                return UINT64_MAX; /* indicate overflow */
+
+        return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline size_t ALIGN_DOWN(size_t l, size_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & ~(ali - 1);
+}
+
+static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & ~(ali - 1);
+}
+
+static inline size_t ALIGN_OFFSET(size_t l, size_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & (ali - 1);
+}
+
+static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & (ali - 1);
 }
 
 #define ALIGN2(l) ALIGN_TO(l, 2)
@@ -399,6 +496,42 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
 #define FLAGS_SET(v, flags) \
         ((~(v) & (flags)) == 0)
 
+/* A wrapper for 'func' to return void.
+ * Only useful when a void-returning function is required by some API. */
+#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func)             \
+        static inline void name(type *p) {                      \
+                func(p);                                        \
+        }
+
+/* When func() returns the void value (NULL, -1, …) of the appropriate type */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
+        static inline void func##p(type *p) {                   \
+                if (*p)                                         \
+                        *p = func(*p);                          \
+        }
+
+/* When func() doesn't return the appropriate type, set variable to empty afterwards.
+ * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty)     \
+        static inline void func##p(type *p) {                   \
+                if (*p != (empty)) {                            \
+                        DISABLE_WARNING_ADDRESS;                \
+                        assert(func);                           \
+                        REENABLE_WARNING;                       \
+                        func(*p);                               \
+                        *p = (empty);                           \
+                }                                               \
+        }
+
+/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty)       \
+        static inline void func##p(type *p) {                           \
+                if (*p != (empty)) {                                    \
+                        func(*p);                                       \
+                        *p = (empty);                                   \
+                }                                                       \
+        }
+
 /* Declare a flexible array usable in a union.
  * This is essentially a work-around for a pointless constraint in C99
  * and might go away in some future version of the standard.
@@ -410,3 +543,16 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
                 dummy_t __empty__ ## name;             \
                 type name[];                           \
         }
+
+/* Declares an ELF read-only string section that does not occupy memory at runtime. */
+#define DECLARE_NOALLOC_SECTION(name, text)   \
+        asm(".pushsection " name ",\"S\"\n\t" \
+            ".ascii " STRINGIFY(text) "\n\t"  \
+            ".zero 1\n\t"                     \
+            ".popsection\n")
+
+#ifdef SBAT_DISTRO
+        #define DECLARE_SBAT(text) DECLARE_NOALLOC_SECTION(".sbat", text)
+#else
+        #define DECLARE_SBAT(text)
+#endif
diff --git a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
index 78e2dbec..6870f54f 100644
--- a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
@@ -11,6 +11,12 @@
 
 #include "macro-fundamental.h"
 
+#define memzero(x, l)                                           \
+        ({                                                      \
+                size_t _l_ = (l);                               \
+                _l_ > 0 ? memset((x), 0, _l_) : (x);            \
+        })
+
 #if !SD_BOOT && HAVE_EXPLICIT_BZERO
 static inline void *explicit_bzero_safe(void *p, size_t l) {
         if (p && l > 0)
@@ -64,3 +70,39 @@ static inline void erase_varp(struct VarEraser *e) {
                 .p = (ptr),                                             \
                 .size = (sz),                                           \
         }
+
+typedef void (*free_array_func_t)(void *p, size_t n);
+
+/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
+typedef struct ArrayCleanup {
+        void **parray;
+        size_t *pn;
+        free_array_func_t pfunc;
+} ArrayCleanup;
+
+static inline void array_cleanup(const ArrayCleanup *c) {
+        assert(c);
+
+        assert(!c->parray == !c->pn);
+
+        if (!c->parray)
+                return;
+
+        if (*c->parray) {
+                assert(c->pfunc);
+                c->pfunc(*c->parray, *c->pn);
+                *c->parray = NULL;
+        }
+
+        *c->pn = 0;
+}
+
+#define CLEANUP_ARRAY(array, n, func)                                   \
+        _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
+                .parray = (void**) &(array),                            \
+                .pn = &(n),                                             \
+                .pfunc = (free_array_func_t) ({                         \
+                                void (*_f)(typeof(array[0]) *a, size_t b) = func; \
+                                _f;                                     \
+                        }),                                             \
+        }
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c
index a4c6d627..84113aed 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256.c
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.c
@@ -36,16 +36,9 @@
 
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 # define SWAP(n)                                                        \
-        (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+        __builtin_bswap32(n)
 # define SWAP64(n)                              \
-        (((n) << 56)                            \
-         | (((n) & 0xff00) << 40)               \
-         | (((n) & 0xff0000) << 24)             \
-         | (((n) & 0xff000000) << 8)            \
-         | (((n) >> 8) & 0xff000000)            \
-         | (((n) >> 24) & 0xff0000)             \
-         | (((n) >> 40) & 0xff00)               \
-         | ((n) >> 56))
+        __builtin_bswap64(n)
 #else
 # define SWAP(n) (n)
 # define SWAP64(n) (n)
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
index 3a3e7f59..da810cb7 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -35,14 +35,14 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
         return (sd_char*) s + l;
 }
 
-sd_char* endswith(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith(const sd_char *s, const sd_char *suffix) {
         size_t sl, pl;
 
         assert(s);
-        assert(postfix);
+        assert(suffix);
 
         sl = strlen(s);
-        pl = strlen(postfix);
+        pl = strlen(suffix);
 
         if (pl == 0)
                 return (sd_char*) s + sl;
@@ -50,20 +50,20 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
         if (sl < pl)
                 return NULL;
 
-        if (strcmp(s + sl - pl, postfix) != 0)
+        if (!streq(s + sl - pl, suffix))
                 return NULL;
 
         return (sd_char*) s + sl - pl;
 }
 
-sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
+sd_char* endswith_no_case(const sd_char *s, const sd_char *suffix) {
         size_t sl, pl;
 
         assert(s);
-        assert(postfix);
+        assert(suffix);
 
         sl = strlen(s);
-        pl = strlen(postfix);
+        pl = strlen(suffix);
 
         if (pl == 0)
                 return (sd_char*) s + sl;
@@ -71,7 +71,7 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
         if (sl < pl)
                 return NULL;
 
-        if (strcasecmp(s + sl - pl, postfix) != 0)
+        if (!strcaseeq(s + sl - pl, suffix))
                 return NULL;
 
         return (sd_char*) s + sl - pl;
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
index 9019542b..419f1cc3 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -59,8 +59,8 @@ static inline size_t strlen_ptr(const sd_char *s) {
 
 sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
 sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
-sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
-sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
+sd_char *endswith(const sd_char *s, const sd_char *suffix) _pure_;
+sd_char *endswith_no_case(const sd_char *s, const sd_char *suffix) _pure_;
 
 static inline bool isempty(const sd_char *a) {
         return !a || a[0] == '\0';
@@ -74,6 +74,10 @@ static inline const sd_char *yes_no(bool b) {
         return b ? STR_C("yes") : STR_C("no");
 }
 
+static inline const sd_char *on_off(bool b) {
+        return b ? STR_C("on") : STR_C("off");
+}
+
 static inline const sd_char* comparison_operator(int result) {
         return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("==");
 }
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index 43f43197..a07eaa33 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -87,12 +87,9 @@ int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags f
                                         ((unsigned) (n[1] - '0') * 10) +
                                         ((unsigned) (n[2] - '0'));
 
-                                /* Don't allow anything that doesn't
-                                 * fit in 8bit. Note that we do allow
-                                 * control characters, as some servers
-                                 * (e.g. cloudflare) are happy to
-                                 * generate labels with them
-                                 * inside. */
+                                /* Don't allow anything that doesn't fit in 8 bits. Note that we do allow
+                                 * control characters, as some servers (e.g. cloudflare) are happy to
+                                 * generate labels with them inside. */
                                 if (k > 255)
                                         return -EINVAL;
 
@@ -213,7 +210,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
         char *q;
 
         /* DNS labels must be between 1 and 63 characters long. A
-         * zero-length label does not exist. See RFC 2182, Section
+         * zero-length label does not exist. See RFC 2181, Section
          * 11. */
 
         if (l <= 0 || l > DNS_LABEL_MAX)
@@ -302,14 +299,14 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) {
 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
         _cleanup_free_ uint32_t *input = NULL;
         size_t input_size, l;
-        bool contains_8bit = false;
+        bool contains_8_bit = false;
         char buffer[DNS_LABEL_MAX+1];
         int r;
 
         assert(encoded);
         assert(decoded);
 
-        /* Converts an U-label into an A-label */
+        /* Converts a U-label into an A-label */
 
         r = dlopen_idn();
         if (r < 0)
@@ -320,9 +317,9 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
 
         for (const char *p = encoded; p < encoded + encoded_size; p++)
                 if ((uint8_t) *p > 127)
-                        contains_8bit = true;
+                        contains_8_bit = true;
 
-        if (!contains_8bit) {
+        if (!contains_8_bit) {
                 if (encoded_size > DNS_LABEL_MAX)
                         return -EINVAL;
 
@@ -361,7 +358,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
         size_t w;
         int r;
 
-        /* To be invoked after unescaping. Converts an A-label into an U-label. */
+        /* To be invoked after unescaping. Converts an A-label into a U-label. */
 
         assert(encoded);
         assert(decoded);
@@ -419,7 +416,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r
                 goto finish;
 
         for (;;) {
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&p, label, sizeof label, flags);
                 if (r < 0)
@@ -517,7 +514,7 @@ int dns_name_compare_func(const char *a, const char *b) {
         y = b + strlen(b);
 
         for (;;) {
-                char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
 
                 if (x == NULL && y == NULL)
                         return 0;
@@ -553,7 +550,7 @@ int dns_name_equal(const char *x, const char *y) {
         assert(y);
 
         for (;;) {
-                char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&x, la, sizeof la, 0);
                 if (r < 0)
@@ -584,7 +581,7 @@ int dns_name_endswith(const char *name, const char *suffix) {
         s = suffix;
 
         for (;;) {
-                char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&n, ln, sizeof ln, 0);
                 if (r < 0)
@@ -622,7 +619,7 @@ int dns_name_startswith(const char *name, const char *prefix) {
         p = prefix;
 
         for (;;) {
-                char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+                char ln[DNS_LABEL_MAX+1], lp[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&p, lp, sizeof lp, 0);
                 if (r < 0)
@@ -654,7 +651,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
         s = old_suffix;
 
         for (;;) {
-                char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+                char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
 
                 if (!saved_before)
                         saved_before = n;
@@ -941,7 +938,7 @@ bool dns_srv_type_is_valid(const char *name) {
                 return false;
 
         for (;;) {
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
 
                 /* This more or less implements RFC 6335, Section 5.1 */
 
@@ -1239,7 +1236,7 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
                 return m;
 
         for (;;) {
-                char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+                char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
                 const char *x, *y;
 
                 if (k >= n || k >= m) {
@@ -1340,7 +1337,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
         assert(ret);
 
         for (;;) {
-                char label[DNS_LABEL_MAX];
+                char label[DNS_LABEL_MAX+1];
 
                 r = dns_label_unescape(&name, label, sizeof label, 0);
                 if (r < 0)
@@ -1425,6 +1422,10 @@ bool dns_name_dont_resolve(const char *name) {
         if (dns_name_endswith(name, "invalid") > 0)
                 return true;
 
+        /* Never respond to some of the domains listed in RFC9476 */
+        if (dns_name_endswith(name, "alt") > 0)
+                return true;
+
         return false;
 }
 #endif /* NM_IGNORED */
diff --git a/src/libnm-udev-aux/README.md b/src/libnm-udev-aux/README.md
new file mode 100644
index 00000000..648f792c
--- /dev/null
+++ b/src/libnm-udev-aux/README.md
@@ -0,0 +1,9 @@
+libnm-udev-aux
+==============
+
+Static helper library with tools around libudev.
+
+This library depends on glib and libudev:
+
+  - [../libnm-std-aux/](../libnm-std-aux/)
+  - [../libnm-glib-aux/](../libnm-glib-aux/)
diff --git a/src/libnmc-base/README.md b/src/libnmc-base/README.md
new file mode 100644
index 00000000..8038c785
--- /dev/null
+++ b/src/libnmc-base/README.md
@@ -0,0 +1,12 @@
+libnmc-base
+===========
+
+A helper library on top of libnm for our clients.
+The "c" in "libnmc-base" stands for clients.
+
+This has no additional dependencies on top of libnm,
+so any client application that uses libnm can statically
+link with this helper at will.
+
+As such, this is very similar in purpose to [../libnm-client-aux-extern](../libnm-client-aux-extern),
+the difference is only in scope.
diff --git a/src/libnmc-base/nm-client-utils.c b/src/libnmc-base/nm-client-utils.c
index 30213e41..4c180e09 100644
--- a/src/libnmc-base/nm-client-utils.c
+++ b/src/libnmc-base/nm-client-utils.c
@@ -466,7 +466,34 @@ NM_UTILS_LOOKUP_STR_DEFINE(
     NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND,
                          N_("The Wi-Fi P2P peer could not be found")),
     NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DEVICE_HANDLER_FAILED,
-                         N_("The device handler dispatcher returned an error")), );
+                         N_("The device handler dispatcher returned an error")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_SLEEPING,
+                         N_("The device is unmanaged because networking is disabled "
+                            "or the system is suspended")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_QUITTING,
+                         N_("The device is unmanaged because NetworkManager is quitting")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_LINK_NOT_INIT,
+                         N_("The device is unmanaged because the link is not initialized by udev")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_EXPLICIT,
+                         N_("The device is unmanaged by explicit user decision (e.g. 'nmcli device "
+                            "set $DEV managed no'")),
+    NM_UTILS_LOOKUP_ITEM(
+        NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS,
+        N_("The device is unmanaged by user decision via settings plugin "
+           "(\"unmanaged-devices\" for keyfile or \"NM_CONTROLLED=no\" for ifcfg-rh)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_CONF,
+                         N_("The device is unmanaged by user decision in NetworkManager.conf "
+                            "('unmanaged' in a [device*] section")),
+    NM_UTILS_LOOKUP_ITEM(
+        NM_DEVICE_STATE_REASON_UNMANAGED_BY_DEFAULT,
+        N_("The device is unmanaged because the device type is unmanaged by default")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV,
+                         N_("The device is unmanaged via udev rule")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_EXTERNAL_DOWN,
+                         N_("The device is unmanaged because it is an external device and is "
+                            "unconfigured (down or without addresses)")),
+
+);
 
 NM_UTILS_LOOKUP_STR_DEFINE(
     nm_active_connection_state_reason_to_string,
diff --git a/src/libnmc-setting/README.md b/src/libnmc-setting/README.md
new file mode 100644
index 00000000..6ed42435
--- /dev/null
+++ b/src/libnmc-setting/README.md
@@ -0,0 +1,17 @@
+libnmc-setting
+==============
+
+A client library on top of libnm (and libnm-base).
+Like libnmc-base, this is a helper library that a libnm
+client could use.
+
+But its purpose is more specific. It's mainly about providing
+a generic API for handling connection properties. As such, it's
+only used by nmcli and in practice also specific to nmcli.
+
+Theoretically, the API is supposed to be generic, so we could
+imagine another client that uses this beside nmcli.
+
+Like libnm-base, this has a similar purpose and application
+as [../libnm-client-aux-extern/](../libnm-client-aux-extern/),
+the difference is that it's even more specific.
diff --git a/src/libnmc-setting/meson.build b/src/libnmc-setting/meson.build
index 7fb460dc..4d5079df 100644
--- a/src/libnmc-setting/meson.build
+++ b/src/libnmc-setting/meson.build
@@ -3,13 +3,13 @@
 if enable_docs
   assert(enable_introspection, '-Ddocs=true requires -Dintrospection=true')
 
-  merge_cmd = join_paths(meson.source_root(), 'tools', 'generate-docs-nm-settings-docs-merge.py')
+  merge_cmd = files(source_root / 'tools' / 'generate-docs-nm-settings-docs-merge.py')
   settings_docs_input_xml = custom_target(
     'settings-docs-input.xml',
     input: [merge_cmd, nm_settings_docs_xml_gir['nmcli'], nm_property_infos_xml['nmcli']],
     output: 'settings-docs-input.xml',
     command: [
-      python.path(),
+      python_path,
       merge_cmd,
       '@OUTPUT@',
       nm_property_infos_xml['nmcli'],
@@ -17,13 +17,13 @@ if enable_docs
     ],
   )
 
-  gen_cmd = join_paths(meson.source_root(), 'tools', 'generate-docs-settings-docs.py')
+  gen_cmd = files(source_root / 'tools' / 'generate-docs-settings-docs.py')
   settings_docs_source = custom_target(
     'settings-docs.h',
     input: [gen_cmd, settings_docs_input_xml],
     output: 'settings-docs.h',
     command: [
-      python.path(),
+      python_path,
       gen_cmd,
       '--output', '@OUTPUT@',
       '--xml', settings_docs_input_xml
diff --git a/src/libnmc-setting/nm-meta-setting-desc.c b/src/libnmc-setting/nm-meta-setting-desc.c
index 2871ccb6..da00c30b 100644
--- a/src/libnmc-setting/nm-meta-setting-desc.c
+++ b/src/libnmc-setting/nm-meta-setting-desc.c
@@ -4922,11 +4922,6 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
         .property_type =                &_pt_gobject_string,
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CA_CERT,
-        .describe_message =
-            N_("Enter file path to CA certificate (optionally prefixed with file://).\n"
-               "  [file://]<file path>\n"
-               "Note that nmcli does not support specifying certificates as raw blob data.\n"
-               "Example: /home/cimrman/cacert.crt\n"),
         .property_type =                &_pt_cert_8021x,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (cert_8021x,
             .scheme_type =              NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT,
@@ -4965,11 +4960,6 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
         .property_type =                &_pt_gobject_string,
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_CLIENT_CERT,
-        .describe_message =
-            N_("Enter file path to client certificate (optionally prefixed with file://).\n"
-               "  [file://]<file path>\n"
-               "Note that nmcli does not support specifying certificates as raw blob data.\n"
-               "Example: /home/cimrman/jara.crt\n"),
         .property_type =                &_pt_cert_8021x,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (cert_8021x,
             .scheme_type =              NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT,
@@ -5022,12 +5012,6 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
         ),
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CA_CERT,
-        .describe_message =
-            N_("Enter file path to CA certificate for inner authentication (optionally prefixed\n"
-               "with file://).\n"
-               "  [file://]<file path>\n"
-               "Note that nmcli does not support specifying certificates as raw blob data.\n"
-               "Example: /home/cimrman/ca-zweite-phase.crt\n"),
         .property_type =                &_pt_cert_8021x,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (cert_8021x,
             .scheme_type =              NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT,
@@ -5070,12 +5054,6 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
         .property_type =                &_pt_gobject_string,
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-        .describe_message =
-            N_("Enter file path to client certificate for inner authentication (optionally prefixed\n"
-               "with file://).\n"
-               "  [file://]<file path>\n"
-               "Note that nmcli does not support specifying certificates as raw blob data.\n"
-               "Example: /home/cimrman/jara-zweite-phase.crt\n"),
         .property_type =                &_pt_cert_8021x,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (cert_8021x,
             .scheme_type =              NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT,
@@ -5114,11 +5092,6 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
         .property_type =                &_pt_gobject_secret_flags,
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PRIVATE_KEY,
-        .describe_message =
-            N_("Enter path to a private key and the key password (if not set yet):\n"
-               "  [file://]<file path> [<password>]\n"
-               "Note that nmcli does not support specifying private key as raw blob data.\n"
-               "Example: /home/cimrman/jara-priv-key Dardanely\n"),
         .property_type =                &_pt_cert_8021x,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (cert_8021x,
             .scheme_type =              NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY,
@@ -5132,11 +5105,6 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
         .property_type =                &_pt_gobject_secret_flags,
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-        .describe_message =
-            N_("Enter path to a private key and the key password (if not set yet):\n"
-               "  [file://]<file path> [<password>]\n"
-               "Note that nmcli does not support specifying private key as raw blob data.\n"
-               "Example: /home/cimrman/jara-priv-key Dardanely\n"),
         .property_type =                &_pt_cert_8021x,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (cert_8021x,
             .scheme_type =              NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY,
@@ -5162,6 +5130,9 @@ static const NMMetaPropertyInfo *const property_infos_802_1X[] = {
     PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_AUTH_TIMEOUT,
         .property_type =                &_pt_gobject_int,
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_802_1X_OPENSSL_CIPHERS,
+        .property_type =                &_pt_gobject_string,
+    ),
     NULL
 };
 
@@ -5663,6 +5634,9 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
     PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_PORTS,
         .property_type =                &_pt_gobject_enum,
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_DOWN_ON_POWEROFF,
+        .property_type =                &_pt_gobject_ternary,
+    ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_SECONDARIES,
         .describe_message =
             N_("Enter secondary connections that should be activated when this connection is\n"
@@ -6340,6 +6314,9 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
     PROPERTY_INFO (NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_REPLACE_LOCAL_RULE,
         .property_type =                &_pt_gobject_ternary,
     ),
+    PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_RELEASE,
+        .property_type =                &_pt_gobject_ternary,
+    ),
     PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES,
         .property_type =                &_pt_gobject_bool,
     ),
@@ -6610,6 +6587,9 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
     PROPERTY_INFO (NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_REPLACE_LOCAL_RULE,
         .property_type =                &_pt_gobject_ternary,
     ),
+    PROPERTY_INFO (NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_RELEASE,
+        .property_type =                &_pt_gobject_ternary,
+    ),
     PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
         .property_type =                &_pt_gobject_bool,
     ),
@@ -6652,6 +6632,12 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
             ),
         ),
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_TEMP_VALID_LIFETIME,
+        .property_type =                &_pt_gobject_int,
+    ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_TEMP_PREFERRED_LIFETIME,
+        .property_type =                &_pt_gobject_int,
+    ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
         .property_type =                &_pt_gobject_enum,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
@@ -8047,6 +8033,7 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST,
         .property_type =                &_pt_multilist,
+        .hide_if_default =              TRUE,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
             PROPERTY_TYP_DATA_SUBTYPE (multilist,
                 .get_num_fcn_u32 =      MULTILIST_GET_NUM_FCN_U32     (NMSettingWired, nm_setting_wired_get_num_mac_blacklist_items),
@@ -8059,6 +8046,20 @@ static const NMMetaPropertyInfo *const property_infos_WIRED[] = {
             .list_items_doc_format =    NM_META_PROPERTY_TYPE_FORMAT_MAC,
         ),
     ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST,
+        .property_type =                &_pt_multilist,
+        .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+            PROPERTY_TYP_DATA_SUBTYPE (multilist,
+                .get_num_fcn_u =        MULTILIST_GET_NUM_FCN_U       (NMSettingWired, nm_setting_wired_get_num_mac_denylist_items),
+                .add_fcn =              MULTILIST_ADD_FCN             (NMSettingWired, nm_setting_wired_add_mac_denylist_item),
+                .remove_by_idx_fcn_u =  MULTILIST_REMOVE_BY_IDX_FCN_U (NMSettingWired, nm_setting_wired_remove_mac_denylist_item),
+                .remove_by_value_fcn =  MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingWired, nm_setting_wired_remove_mac_denylist_item_by_value),
+                .validate2_fcn =        _multilist_validate2_fcn_mac_addr,
+                .strsplit_plain =       TRUE,
+            ),
+            .list_items_doc_format =    NM_META_PROPERTY_TYPE_FORMAT_MAC,
+        ),
+    ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRED_MTU,
         .is_cli_option =                TRUE,
         .property_alias =               "mtu",
@@ -8228,12 +8229,27 @@ static const NMMetaPropertyInfo *const property_infos_WIRELESS[] = {
     ),
     PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST,
         .property_type =                &_pt_multilist,
+        .hide_if_default =              TRUE,
+        .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+            PROPERTY_TYP_DATA_SUBTYPE (multilist,
+                .get_num_fcn_u32 =       MULTILIST_GET_NUM_FCN_U32     (NMSettingWireless, nm_setting_wireless_get_num_mac_denylist_items),
+                .add_fcn =               MULTILIST_ADD_FCN             (NMSettingWireless, nm_setting_wireless_add_mac_denylist_item),
+                .remove_by_idx_fcn_u32 = MULTILIST_REMOVE_BY_IDX_FCN_U32 (NMSettingWireless, nm_setting_wireless_remove_mac_denylist_item),
+                .remove_by_value_fcn =   MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingWireless, nm_setting_wireless_remove_mac_denylist_item_by_value),
+                .validate2_fcn =        _multilist_validate2_fcn_mac_addr,
+                .strsplit_plain =       TRUE,
+            ),
+            .list_items_doc_format =    NM_META_PROPERTY_TYPE_FORMAT_MAC,
+        ),
+    ),
+    PROPERTY_INFO_WITH_DESC (NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST,
+        .property_type =                &_pt_multilist,
         .property_typ_data = DEFINE_PROPERTY_TYP_DATA (
             PROPERTY_TYP_DATA_SUBTYPE (multilist,
-                .get_num_fcn_u32 =      MULTILIST_GET_NUM_FCN_U32     (NMSettingWireless, nm_setting_wireless_get_num_mac_blacklist_items),
-                .add_fcn =              MULTILIST_ADD_FCN             (NMSettingWireless, nm_setting_wireless_add_mac_blacklist_item),
-                .remove_by_idx_fcn_u32 = MULTILIST_REMOVE_BY_IDX_FCN_U32 (NMSettingWireless, nm_setting_wireless_remove_mac_blacklist_item),
-                .remove_by_value_fcn =  MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingWireless, nm_setting_wireless_remove_mac_blacklist_item_by_value),
+                .get_num_fcn_u =        MULTILIST_GET_NUM_FCN_U       (NMSettingWireless, nm_setting_wireless_get_num_mac_denylist_items),
+                .add_fcn =              MULTILIST_ADD_FCN             (NMSettingWireless, nm_setting_wireless_add_mac_denylist_item),
+                .remove_by_idx_fcn_u =  MULTILIST_REMOVE_BY_IDX_FCN_U (NMSettingWireless, nm_setting_wireless_remove_mac_denylist_item),
+                .remove_by_value_fcn =  MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingWireless, nm_setting_wireless_remove_mac_denylist_item_by_value),
                 .validate2_fcn =        _multilist_validate2_fcn_mac_addr,
                 .strsplit_plain =       TRUE,
             ),
diff --git a/src/libnmc-setting/settings-docs.h b/src/libnmc-setting/settings-docs.h
deleted file mode 100644
index c4014166..00000000
--- a/src/libnmc-setting/settings-docs.h
+++ /dev/null
@@ -1,469 +0,0 @@
-/* Generated file. Do not edit. */
-
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTH_RETRIES N_("The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT N_("Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See \"secondaries\" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better \"connection.autoconnect-priority\" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their \"connection.timestamp\" is identical, the choice is undefined. Depending on \"connection.multi-connect\", a profile can (auto)connect only once at a time or multiple times.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_PORTS N_("Whether or not ports of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for controller connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave port connections untouched, 1: activate all the port connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-ports is read to determine the real value. If it is default as well, this fallbacks to 0.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY N_("The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES N_("The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES N_("Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_CONTROLLER N_("Interface name of the controller device or UUID of the controller connection.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_DNS_OVER_TLS N_("Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: \"yes\" (2) use DNSOverTls and disabled fallback, \"opportunistic\" (1) use DNSOverTls but allow fallback to unencrypted resolution, \"no\" (0) don't ever use DNSOverTls. If unspecified \"default\" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT N_("If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_ID N_("A human readable unique identifier for the connection, like \"Work Wi-Fi\" or \"T-Mobile 3G\".")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_INTERFACE_NAME N_("The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_LLDP N_("Whether LLDP is enabled for the connection.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_LLMNR N_("Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: \"yes\" (2) register hostname and resolving for the connection, \"no\" (0) disable LLMNR for the interface, \"resolve\" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, \"default\" ultimately depends on the DNS plugin (which for systemd-resolved currently means \"yes\"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MASTER N_("Interface name of the master device or UUID of the master connection. Deprecated 1.46. Use \"controller\" instead, this is just an alias.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MDNS N_("Whether mDNS is enabled for the connection. The permitted values are: \"yes\" (2) register hostname and resolving for the connection, \"no\" (0) disable mDNS for the interface, \"resolve\" (1) do not register hostname but allow resolving of mDNS host names and \"default\" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, \"default\" ultimately depends on the DNS plugin (which for systemd-resolved currently means \"no\"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_METERED N_("Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MPTCP_FLAGS N_("Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. If \"disabled\" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. The \"enabled\" (0x2) flag means that MPTCP handling is enabled. This flag can also be implied from the presence of other flags. Even when enabled, MPTCP handling will by default still be disabled unless \"/proc/sys/net/mptcp/enabled\" sysctl is on. NetworkManager does not change the sysctl and this is up to the administrator or distribution. To configure endpoints even if the sysctl is disabled, \"also-without-sysctl\" (0x4) flag can be used. In that case, NetworkManager doesn't look at the sysctl and configures endpoints regardless. Even when enabled, NetworkManager will only configure MPTCP endpoints for a certain address family, if there is a unicast default route (0.0.0.0/0 or ::/0) in the main routing table. The flag \"also-without-default-route\" (0x8) can override that. When MPTCP handling is enabled then endpoints are configured with the specified address flags \"signal\" (0x10), \"subflow\" (0x20), \"backup\" (0x40), \"fullmesh\" (0x80). See ip-mptcp(8) manual for additional information about the flags. If the flags are zero (0x0), the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is \"enabled,subflow\". Note that this means that MPTCP is by default done depending on the \"/proc/sys/net/mptcp/enabled\" sysctl. NetworkManager does not change the MPTCP limits nor enable MPTCP via \"/proc/sys/net/mptcp/enabled\". That is a host configuration which the admin can change via sysctl and ip-mptcp. Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2).")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MUD_URL N_("If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with \"https://\". The special value \"none\" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is \"none\".")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MULTI_CONNECT N_("Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_PERMISSIONS N_("An array of strings defining what access a given user has to this connection.  If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list.  When this is not empty, the connection can be active only when one of the specified users is logged into an active session.  Each entry is of the form \"[type]:[id]:[reserved]\"; for example, \"user:dcbw:blah\". At this time only the \"user\" [type] is allowed.  Any other values are ignored and reserved for future use.  [id] is the username that this permission refers to, which may not contain the \":\" character. Any [reserved] information present must be ignored and is reserved for future use.  All of [type], [id], and [reserved] must be valid UTF-8.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_PORT_TYPE N_("Setting name of the device type of this port's controller connection (eg, \"bond\"), or NULL if this connection is not a port.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_READ_ONLY N_("This property is deprecated and has no meaning.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_SECONDARIES N_("List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_SLAVE_TYPE N_("Setting name of the device type of this slave's master connection (eg, \"bond\"), or NULL if this connection is not a slave. Deprecated 1.46. Use \"port-type\" instead, this is just an alias.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_STABLE_ID N_("This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used to derive the DHCP client identifier with ipv4.dhcp-client-id=stable, the DHCPv6 DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid] and the DHCP IAID with ipv4.iaid=stable and ipv6.iaid=stable. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at activation time. Currently, supported are \"${CONNECTION}\", \"${DEVICE}\", \"${MAC}\", \"${NETWORK_SSID}\", \"${BOOT}\", \"${RANDOM}\".  These effectively create unique IDs per-connection, per-device, per-SSID, per-boot, or every time.  The \"${CONNECTION}\" uses the profile's connection.uuid, the \"${DEVICE}\" uses the interface name of the device and \"${MAC}\" the permanent MAC address of the device. \"${NETWORK_SSID}\" uses the SSID for Wi-Fi networks and falls back to \"${CONNECTION}\" on other networks. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as \"$$\".  For example, set it to \"${CONNECTION}-${BOOT}-${DEVICE}\" to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is \"default${CONNECTION}\" go generate an ID unique per connection profile.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_TIMESTAMP N_("The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved).")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_TYPE N_("Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, \"802-3-ethernet\" or \"802-11-wireless\" or \"bluetooth\", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, \"vpn\" or \"bridge\", etc).")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_UUID N_("The connection.uuid is the real identifier of a profile. It cannot change and it must be unique. It is therefore often best to refer to a profile by UUID, for example with `nmcli connection up uuid $UUID`. The UUID cannot be changed, except in offline mode. In that case, the special values \"new\", \"generate\" and \"\" are allowed to generate a new random UUID.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY N_("Time in milliseconds to wait for connection to be considered activated. The wait will start after the pre-up dispatcher event. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT N_("Timeout in milliseconds to wait for device at startup. During boot, devices may take a while to be detected by the driver. This property will cause to delay NetworkManager-wait-online.service and nm-online to give the device a chance to appear. This works by waiting for the given timeout until a compatible device for the profile is available and managed. The value 0 means no wait time. The default value is -1, which currently has the same meaning as no wait time.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_ZONE N_("The trust level of a the connection.  Free form case-insensitive string (for example \"Home\", \"Work\", \"Public\").  NULL or unspecified zone means the connection will be placed in the default zone as defined by the firewall. When updating this property on a currently activated connection, the change takes effect immediately.")
-#define DESCRIBE_DOC_NM_SETTING_6LOWPAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this 6LowPAN interface should be created.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_ALTSUBJECT_MATCHES N_("List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_ANONYMOUS_IDENTITY N_("Anonymous identity string for EAP authentication methods.  Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_AUTH_TIMEOUT N_("A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT N_("Contains the CA certificate if used by the EAP method specified in the \"eap\" property. Certificate data is specified using a \"scheme\"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT_PASSWORD N_("The password used to access the CA certificate stored in \"ca-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"ca-cert-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CA_PATH N_("UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the \"ca-cert\" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT N_("Contains the client certificate if used by the EAP method specified in the \"eap\" property. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT_PASSWORD N_("The password used to access the client certificate stored in \"client-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"client-cert-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_DOMAIN_MATCH N_("Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a \";\" delimited list.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH N_("Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a \";\" delimited list.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_EAP N_("The allowed EAP method to be used when authenticating to the network with 802.1x.  Valid methods are: \"leap\", \"md5\", \"tls\", \"peap\", \"ttls\", \"pwd\", and \"fast\".  Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_IDENTITY N_("Identity string for EAP authentication methods.  Often the user's user or login name.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_OPTIONAL N_("Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PAC_FILE N_("UTF-8 encoded file path containing PAC for EAP-FAST.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD N_("UTF-8 encoded password used for EAP authentication methods. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_RAW N_("Password used for EAP authentication methods, given as a byte array to allow passwords in other encodings than UTF-8 to be used. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD_RAW_FLAGS N_("Flags indicating how to handle the \"password-raw\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_AUTH_FLAGS N_("Specifies authentication flags to use in \"phase 1\" outer authentication using NMSetting8021xAuthFlags options. The individual TLS versions can be explicitly disabled. TLS time checks can be also disabled. If a certain TLS disable flag is not set, it is up to the supplicant to allow or forbid it. The TLS options map to tls_disable_tlsv1_x and tls_disable_time_checks settings. See the wpa_supplicant documentation for more details.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING N_("Enables or disables in-line provisioning of EAP-FAST credentials when FAST is specified as the EAP method in the \"eap\" property. Recognized values are \"0\" (disabled), \"1\" (allow unauthenticated provisioning), \"2\" (allow authenticated provisioning), and \"3\" (allow both authenticated and unauthenticated provisioning).  See the wpa_supplicant documentation for more details.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_PEAPLABEL N_("Forces use of the new PEAP label during key derivation.  Some RADIUS servers may require forcing the new PEAP label to interoperate with PEAPv1.  Set to \"1\" to force use of the new PEAP label.  See the wpa_supplicant documentation for more details.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE1_PEAPVER N_("Forces which PEAP version is used when PEAP is set as the EAP method in the \"eap\" property.  When unset, the version reported by the server will be used.  Sometimes when using older RADIUS servers, it is necessary to force the client to use a particular PEAP version.  To do so, this property may be set to \"0\" or \"1\" to force that specific PEAP version.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES N_("List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner \"phase 2\" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_AUTH N_("Specifies the allowed \"phase 2\" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the \"eap\" property.  For TTLS this property selects one of the supported non-EAP inner methods: \"pap\", \"chap\", \"mschap\", \"mschapv2\" while \"phase2-autheap\" selects an EAP inner method.  For PEAP this selects an inner EAP method, one of: \"gtc\", \"otp\", \"md5\" and \"tls\". Each \"phase 2\" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both \"phase2-auth\" and \"phase2-autheap\" cannot be specified.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_AUTHEAP N_("Specifies the allowed \"phase 2\" inner EAP-based authentication method when TTLS is specified in the \"eap\" property.  Recognized EAP-based \"phase 2\" methods are \"md5\", \"mschapv2\", \"otp\", \"gtc\", and \"tls\". Each \"phase 2\" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT N_("Contains the \"phase 2\" CA certificate if used by the EAP method specified in the \"phase2-auth\" or \"phase2-autheap\" properties. Certificate data is specified using a \"scheme\"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD N_("The password used to access the \"phase2\" CA certificate stored in \"phase2-ca-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-ca-cert-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_PATH N_("UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the \"phase2-ca-cert\" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT N_("Contains the \"phase 2\" client certificate if used by the EAP method specified in the \"phase2-auth\" or \"phase2-autheap\" properties. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD N_("The password used to access the \"phase2\" client certificate stored in \"phase2-client-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-client-cert-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH N_("Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner \"phase 2\" authentication. If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a \";\" delimited list.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH N_("Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner \"phase 2\" authentication.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a \";\" delimited list.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY N_("Contains the \"phase 2\" inner private key when the \"phase2-auth\" or \"phase2-autheap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"phase2-private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"phase2-private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the \"phase 2\" private key specified in the \"phase2-private-key\" property when the private key either uses the path scheme, or is a PKCS#12 format key.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-private-key-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PIN N_("PIN used for EAP authentication methods.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PIN_FLAGS N_("Flags indicating how to handle the \"pin\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY N_("Contains the private key when the \"eap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: \"private-key\" is not a \"secret\" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the private key specified in the \"private-key\" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"private-key-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_SYSTEM_CA_CERTS N_("When TRUE, overrides the \"ca-path\" and \"phase2-ca-path\" properties using the system CA directory specified at configure time with the --system-ca-path switch.  The certificates in this directory are added to the verification chain in addition to any certificates specified by the \"ca-cert\" and \"phase2-ca-cert\" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides \"ca-cert\" and \"phase2-ca-cert\" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant).")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_ENCAPSULATION N_("Encapsulation of ADSL connection.  Can be \"vcmux\" or \"llc\".")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_PASSWORD N_("Password used to authenticate with the ADSL service.")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_PROTOCOL N_("ADSL connection protocol.  Can be \"pppoa\", \"pppoe\" or \"ipoatm\".")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_USERNAME N_("Username used to authenticate with the ADSL service.")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_VCI N_("VCI of ADSL connection")
-#define DESCRIBE_DOC_NM_SETTING_ADSL_VPI N_("VPI of ADSL connection")
-#define DESCRIBE_DOC_NM_SETTING_BLUETOOTH_BDADDR N_("The Bluetooth address of the device.")
-#define DESCRIBE_DOC_NM_SETTING_BLUETOOTH_TYPE N_("Either \"dun\" for Dial-Up Networking connections or \"panu\" for Personal Area Networking connections to devices supporting the NAP profile.")
-#define DESCRIBE_DOC_NM_SETTING_BOND_OPTIONS N_("Dictionary of key/value pairs of bonding options.  Both keys and values must be strings. Option names must contain only alphanumeric characters (ie, [a-zA-Z0-9]).")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_AGEING_TIME N_("The Ethernet MAC address aging time, in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_FORWARD_DELAY N_("The Spanning Tree Protocol (STP) forwarding delay, in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_GROUP_ADDRESS N_("If specified, The MAC address of the multicast group this bridge uses for STP. The address must be a link-local address in standard Ethernet MAC address format, ie an address of the form 01:80:C2:00:00:0X, with X in [0, 4..F]. If not specified the default value is 01:80:C2:00:00:00.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_GROUP_FORWARD_MASK N_("A mask of group addresses to forward. Usually, group addresses in the range from 01:80:C2:00:00:00 to 01:80:C2:00:00:0F are not forwarded according to standards. This property is a mask of 16 bits, each corresponding to a group address in that range that must be forwarded. The mask can't have bits 0, 1 or 2 set because they are used for STP, MAC pause frames and LACP.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_HELLO_TIME N_("The Spanning Tree Protocol (STP) hello time, in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAC_ADDRESS N_("If specified, the MAC address of bridge. When creating a new bridge, this MAC address will be set. If this field is left unspecified, the \"ethernet.cloned-mac-address\" is referred instead to generate the initial MAC address. Note that setting \"ethernet.cloned-mac-address\" anyway overwrites the MAC address of the bridge later while activating the bridge.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MAX_AGE N_("The Spanning Tree Protocol (STP) maximum message age, in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_HASH_MAX N_("Set maximum size of multicast hash table (value must be a power of 2).")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT N_("Set the number of queries the bridge will send before stopping forwarding a multicast group after a \"leave\" message has been received.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL N_("Set interval (in deciseconds) between queries to find remaining members of a group, after a \"leave\" message is received.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL N_("Set delay (in deciseconds) after which the bridge will leave a group, if no membership reports for this group are received.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_QUERIER N_("Enable or disable sending of multicast queries by the bridge. If not specified the option is disabled.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_QUERIER_INTERVAL N_("If no queries are seen after this delay (in deciseconds) has passed, the bridge will start to send its own queries.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_QUERY_INTERVAL N_("Interval (in deciseconds) between queries sent by the bridge after the end of the startup phase.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL N_("Set the Max Response Time/Max Response Delay (in deciseconds) for IGMP/MLD queries sent by the bridge.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR N_("If enabled the bridge's own IP address is used as the source address for IGMP queries otherwise the default of 0.0.0.0 is used.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_ROUTER N_("Sets bridge's multicast router. Multicast-snooping must be enabled for this option to work. Supported values are: 'auto', 'disabled', 'enabled' to which kernel assigns the numbers 1, 0, and 2, respectively. If not specified the default value is 'auto' (1).")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_SNOOPING N_("Controls whether IGMP snooping is enabled for this bridge. Note that if snooping was automatically disabled due to hash collisions, the system may refuse to enable the feature until the collisions are resolved.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT N_("Set the number of IGMP queries to send during startup phase.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL N_("Sets the time (in deciseconds) between queries sent out at startup to determine membership information.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PRIORITY N_("Sets the Spanning Tree Protocol (STP) priority for this bridge.  Lower values are \"better\"; the lowest priority bridge will be elected the root bridge.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_STP N_("Controls whether Spanning Tree Protocol (STP) is enabled for this bridge.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID N_("The default PVID for the ports of the bridge, that is the VLAN id assigned to incoming untagged frames.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_FILTERING N_("Control whether VLAN filtering is enabled on the bridge.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_PROTOCOL N_("If specified, the protocol used for VLAN filtering. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLAN_STATS_ENABLED N_("Controls whether per-VLAN stats accounting is enabled.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_VLANS N_("Array of bridge VLAN objects. In addition to the VLANs specified here, the bridge will also have the default-pvid VLAN configured  by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE N_("Enables or disables \"hairpin mode\" for the port, which allows frames to be sent back out through the port the frame was received on.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PATH_COST N_("The Spanning Tree Protocol (STP) port cost for destinations via this port.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_PRIORITY N_("The Spanning Tree Protocol (STP) priority of this bridge port.")
-#define DESCRIBE_DOC_NM_SETTING_BRIDGE_PORT_VLANS N_("Array of bridge VLAN objects. In addition to the VLANs specified here, the port will also have the default-pvid VLAN configured on the bridge by the bridge.vlan-default-pvid property. In nmcli the VLAN list can be specified with the following syntax: $vid [pvid] [untagged] [, $vid [pvid] [untagged]]... where $vid is either a single id between 1 and 4094 or a range, represented as a couple of ids separated by a dash.")
-#define DESCRIBE_DOC_NM_SETTING_CDMA_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
-#define DESCRIBE_DOC_NM_SETTING_CDMA_NUMBER N_("The number to dial to establish the connection to the CDMA-based mobile broadband network, if any.  If not specified, the default number (#777) is used when required.")
-#define DESCRIBE_DOC_NM_SETTING_CDMA_PASSWORD N_("The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here.")
-#define DESCRIBE_DOC_NM_SETTING_CDMA_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_CDMA_USERNAME N_("The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_FLAGS N_("Specifies the NMSettingDcbFlags for the DCB FCoE application.  Flags may be any combination of \"enable\" (0x1), \"advertise\" (0x2), and \"willing\" (0x4).")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_MODE N_("The FCoE controller mode; either \"fabric\" or \"vn2vn\". Since 1.34, NULL is the default and means \"fabric\". Before 1.34, NULL was rejected as invalid and the default was \"fabric\".")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_FCOE_PRIORITY N_("The highest User Priority (0 - 7) which FCoE frames should use, or -1 for default priority.  Only used when the \"app-fcoe-flags\" property includes the \"enable\" (0x1) flag.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_FIP_FLAGS N_("Specifies the NMSettingDcbFlags for the DCB FIP application.  Flags may be any combination of \"enable\" (0x1), \"advertise\" (0x2), and \"willing\" (0x4).")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_FIP_PRIORITY N_("The highest User Priority (0 - 7) which FIP frames should use, or -1 for default priority.  Only used when the \"app-fip-flags\" property includes the \"enable\" (0x1) flag.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_ISCSI_FLAGS N_("Specifies the NMSettingDcbFlags for the DCB iSCSI application.  Flags may be any combination of \"enable\" (0x1), \"advertise\" (0x2), and \"willing\" (0x4).")
-#define DESCRIBE_DOC_NM_SETTING_DCB_APP_ISCSI_PRIORITY N_("The highest User Priority (0 - 7) which iSCSI frames should use, or -1 for default priority. Only used when the \"app-iscsi-flags\" property includes the \"enable\" (0x1) flag.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_BANDWIDTH N_("An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the percentage of bandwidth of the priority's assigned group that the priority may use.  The sum of all percentages for priorities which belong to the same group must total 100 percents.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_FLOW_CONTROL N_("An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the corresponding priority should transmit priority pause.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS N_("Specifies the NMSettingDcbFlags for DCB Priority Flow Control (PFC). Flags may be any combination of \"enable\" (0x1), \"advertise\" (0x2), and \"willing\" (0x4).")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH N_("An array of 8 uint values, where the array index corresponds to the Priority Group ID (0 - 7) and the value indicates the percentage of link bandwidth allocated to that group.  Allowed values are 0 - 100, and the sum of all values must total 100 percents.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_GROUP_FLAGS N_("Specifies the NMSettingDcbFlags for DCB Priority Groups.  Flags may be any combination of \"enable\" (0x1), \"advertise\" (0x2), and \"willing\" (0x4).")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_GROUP_ID N_("An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the Priority Group ID.  Allowed Priority Group ID values are 0 - 7 or 15 for the unrestricted group.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH N_("An array of 8 boolean values, where the array index corresponds to the User Priority (0 - 7) and the value indicates whether or not the priority may use all of the bandwidth allocated to its assigned group.")
-#define DESCRIBE_DOC_NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS N_("An array of 8 uint values, where the array index corresponds to the User Priority (0 - 7) and the value indicates the traffic class (0 - 7) to which the priority is mapped.")
-#define DESCRIBE_DOC_NM_SETTING_GENERIC_DEVICE_HANDLER N_("Name of the device handler that will be invoked to add and delete the device for this connection. The name can only contain ASCII alphanumeric characters and '-', '_', '.'. It cannot start with '.'. See the NetworkManager-dispatcher(8) man page for more details about how to write the device handler. By setting this property the generic connection becomes \"virtual\", meaning that it can be activated without an existing device; the device will be created at the time the connection is started by invoking the device-handler.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_APN N_("The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network.  The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan. The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9. If the APN is unset (the default) then it may be detected based on \"auto-config\" setting. The property can be explicitly set to the empty string to prevent that and use no APN.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_AUTO_CONFIG N_("When TRUE, the settings such as APN, username, or password will default to values that match the network the modem will register to in the Mobile Broadband Provider database.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_DEVICE_ID N_("The device unique identifier (as given by the WWAN management service) which this connection applies to.  If given, the connection will only apply to the specified device.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_HOME_ONLY N_("When TRUE, only connections to the home network will be allowed. Connections to roaming networks will not be made.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_INITIAL_EPS_BEARER_APN N_("For LTE modems, this sets the APN for the initial EPS bearer that is set up when attaching to the network.  Setting this parameter implies initial-eps-bearer-configure to be TRUE.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_INITIAL_EPS_BEARER_CONFIGURE N_("For LTE modems, this setting determines whether the initial EPS bearer shall be configured when bringing up the connection.  It is inferred TRUE if initial-eps-bearer-apn is set.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_NETWORK_ID N_("The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.  If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network. This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_NUMBER N_("Legacy setting that used to help establishing PPP data sessions for GSM-based modems.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD N_("The password used to authenticate with the network, if required.  Many providers do not require a password, or accept any password.  But if a password is required, it is specified here.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_PIN N_("If the SIM is locked with a PIN it must be unlocked before any other operations are requested.  Specify the PIN here to allow operation of the device.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_PIN_FLAGS N_("Flags indicating how to handle the \"pin\" property.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_SIM_ID N_("The SIM card unique identifier (as given by the WWAN management service) which this connection applies to.  If given, the connection will apply to any device also allowed by \"device-id\" which contains a SIM card matching the given identifier.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_SIM_OPERATOR_ID N_("A MCC/MNC string like \"310260\" or \"21601\" identifying the specific mobile network operator which this connection applies to.  If given, the connection will apply to any device also allowed by \"device-id\" and \"sim-id\" which contains a SIM card provisioned by the given operator.")
-#define DESCRIBE_DOC_NM_SETTING_GSM_USERNAME N_("The username used to authenticate with the network, if required.  Many providers do not require a username, or accept any username.  But if a username is required, it is specified here.")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MAC_ADDRESS N_("If specified, this connection will only apply to the IPoIB device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple frames.")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_P_KEY N_("The InfiniBand p-key to use for this device. A value of -1 means to use the default p-key (aka \"the p-key at index 0\"). Otherwise, it is a 16-bit unsigned integer, whose high bit 0x8000 is set if it is a \"full membership\" p-key. The values 0 and 0x8000 are not allowed. With the p-key set, the interface name is always \"$parent.$p_key\". Setting \"connection.interface-name\" to another name is not supported. Note that kernel will internally always set the full membership bit, although the interface name does not reflect that. Usually the user would want to configure a full membership p-key with 0x8000 flag set.")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_PARENT N_("The interface name of the parent device of this device. Normally NULL, but if the \"p_key\" property is set, then you must specify the base device by setting either this property or \"mac-address\".")
-#define DESCRIBE_DOC_NM_SETTING_INFINIBAND_TRANSPORT_MODE N_("The IP-over-InfiniBand transport mode. Either \"datagram\" or \"connected\".")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ADDRESSES N_("A list of IPv4 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"192.168.1.5/24, 10.1.0.5/24\". The addresses are listed in decreasing priority, meaning the first address will be the primary address.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DAD_TIMEOUT N_("Maximum timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail. The property is currently implemented only for IPv4. A zero value means that no duplicate address detection is performed, -1 means the default value (either the value configured globally in NetworkManger.conf or 200ms).  A value greater than zero is a timeout in milliseconds.  Note that the time intervals are subject to randomization as per RFC 5227 and so the actual duration can be between half and the full time specified in this property.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID N_("A string sent to the DHCP server to identify the local machine which the DHCP server may use to customize the DHCP lease and options. When the property is a hex string ('aa:bb:cc') it is interpreted as a binary client ID, in which case the first byte is assumed to be the 'type' field as per RFC 2132 section 9.14 and the remaining bytes may be an hardware address (e.g. '01:xx:xx:xx:xx:xx:xx' where 1 is the Ethernet ARP type and the rest is a MAC address). If the property is not a hex string it is considered as a non-hardware-address client ID and the 'type' field is set to 0. The special values \"mac\" and \"perm-mac\" are supported, which use the current or permanent MAC address of the device to generate a client identifier with type ethernet (01). Currently, these options only work for ethernet type of links. The special value \"ipv6-duid\" uses the DUID from \"ipv6.dhcp-duid\" property as an RFC4361-compliant client identifier. As IAID it uses \"ipv4.dhcp-iaid\" and falls back to \"ipv6.dhcp-iaid\" if unset. The special value \"duid\" generates a RFC4361-compliant client identifier based on \"ipv4.dhcp-iaid\" and uses a DUID generated by hashing /etc/machine-id. The special value \"stable\" is supported to generate a type 0 client identifier based on the stable-id (see connection.stable-id) and a per-host key. If you set the stable-id, you may want to include the \"${DEVICE}\" or \"${MAC}\" specifier to get a per-device key. The special value \"none\" prevents any client identifier from being sent. Note that this is normally not recommended. If unset, a globally configured default from NetworkManager.conf is used. If still unset, the default depends on the DHCP plugin. The internal dhcp client will default to \"mac\" and the dhclient plugin will try to use one from its config file if present, or won't sent any client-id otherwise.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_DSCP N_("Specifies the value for the DSCP field (traffic class) of the IP header. When empty, the global default value is used; if no global default is specified, it is assumed to be \"CS0\". Allowed values are: \"CS0\", \"CS4\" and \"CS6\". The property is currently valid only for IPv4, and it is supported only by the \"internal\" DHCP plugin.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_FQDN N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified FQDN will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-hostname\" are mutually exclusive and cannot be set at the same time.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME_FLAGS N_("Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) and \"fqdn-no-update\" (0x4).  When no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is not set, the standard FQDN flags are set in the request: \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) for IPv4 and \"fqdn-serv-update\" (0x1) for IPv6. When this property is set to the default value \"none\" (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also \"none\" (0x0), then the standard FQDN flags described above are sent in the DHCP requests.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_IAID N_("A string containing the \"Identity Association Identifier\" (IAID) used by the DHCP client. The string can be a 32-bit number (either decimal, hexadecimal or as colon separated hexadecimal numbers). Alternatively it can be set to the special values \"mac\", \"perm-mac\", \"ifname\" or \"stable\". When set to \"mac\" (or \"perm-mac\"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to \"ifname\", the IAID is computed by hashing the interface name. The special value \"stable\" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be \"ifname\". For DHCPv4, the IAID is only used with \"ipv4.dhcp-client-id\" values \"duid\" and \"ipv6-duid\" to generate the client-id. For DHCPv6, note that at the moment this property is only supported by the \"internal\" DHCPv6 plugin. The \"dhclient\" DHCPv6 plugin always derives the IAID from the MAC address. The actually used DHCPv6 IAID for a currently activated interface is exposed in the lease information of the device.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER N_("The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_OPTIONS N_("DNS options for /etc/resolv.conf as described in resolv.conf(5) manual. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"ndots\", \"no-aaaa\", \"no-check-names\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\". See the resolv.conf(5) manual. Note that there is a distinction between an unset (default) list and an empty list. In nmcli, to unset the list set the value to \"\". To set an empty list, set it to \" \". Currently, an unset list has the same meaning as an empty list. That might change in the future. The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added. The valid \"ipv4.dns-options\" and \"ipv6.dns-options\" get merged together.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the \"rotate\" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_SEARCH N_("List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting \"ignore-auto-dns\". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15).")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if \"never-default\" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See \"ip4-auto-default-route\".")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured routes are ignored and only routes specified in the \"routes\" property, if any, are used.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_LINK_LOCAL N_("Enable and disable the IPv4 link-local configuration independently of the ipv4.method configuration. This allows a link-local address (169.254.x.y/16) to be obtained in addition to other addresses, such as those manually configured or obtained from a DHCP server. When set to \"auto\", the value is dependent on \"ipv4.method\". When set to \"default\", it honors the global connection default, before falling back to \"auto\". Note that if \"ipv4.method\" is \"disabled\", then link local addressing is always disabled too. The default is \"default\".")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_MAY_FAIL N_("If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_METHOD N_("The IPv4 connection method.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_NEVER_DEFAULT N_("If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_REPLACE_LOCAL_RULE N_("Connections will default to keep the autogenerated priority 0 local rule unless this setting is set to TRUE.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_REQUIRED_TIMEOUT N_("The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if \"may-fail\" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero).")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_METRIC N_("The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTES N_("A list of IPv4 destination addresses, prefix length, optional IPv4 next hop addresses, optional route metric, optional attribute. The valid syntax is: \"ip[/prefix] [next-hop] [metric] [attribute=val]...[,ip[/prefix]...]\". For example \"192.0.2.0/24 10.1.1.1 77, 198.51.100.0/24\".")
-#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE N_("Configure method for creating the IPv6 interface identifer of addresses with RFC4862 IPv6 Stateless Address Autoconfiguration and Link Local addresses. The permitted values are: \"eui64\" (0), \"stable-privacy\" (1), \"default\" (3) or \"default-or-eui64\" (2). If the property is set to \"eui64\", the addresses will be generated using the interface token derived from hardware address. This makes the host part of the address to stay constant, making it possible to track the host's presence when it changes networks. The address changes when the interface hardware is replaced. If a duplicate address is detected, there is also no fallback to generate another address. When configured, the \"ipv6.token\" is used instead of the MAC address to generate addresses for stateless autoconfiguration. If the property is set to \"stable-privacy\", the interface identifier is generated as specified by RFC7217. This works by hashing a host specific key (see NetworkManager(8) manual), the interface name, the connection's \"connection.stable-id\" property and the address prefix.  This improves privacy by making it harder to use the address to track the host's presence and the address is stable when the network interface hardware is replaced. The special values \"default\" and \"default-or-eui64\" will fallback to the global connection default as documented in the NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is \"stable-privacy\" or \"eui64\", respectively. If not specified, when creating a new profile the default is \"default\". Note that this setting is distinct from the Privacy Extensions as configured by \"ip6-privacy\" property and it does not affect the temporary addresses configured with this option.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ADDRESSES N_("A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example \"2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64\". The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5).")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_AUTO_ROUTE_EXT_GW N_("VPN connections will default to add the route automatically unless this setting is set to FALSE. For other connection types, adding such an automatic route is currently not supported and setting this to TRUE has no effect.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DAD_TIMEOUT N_("Maximum timeout in milliseconds used to check for the presence of duplicate IP addresses on the network.  If an address conflict is detected, the activation will fail. The property is currently implemented only for IPv4. A zero value means that no duplicate address detection is performed, -1 means the default value (either the value configured globally in NetworkManger.conf or 200ms).  A value greater than zero is a timeout in milliseconds.  Note that the time intervals are subject to randomization as per RFC 5227 and so the actual duration can be between half and the full time specified in this property.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DSCP N_("Specifies the value for the DSCP field (traffic class) of the IP header. When empty, the global default value is used; if no global default is specified, it is assumed to be \"CS0\". Allowed values are: \"CS0\", \"CS4\" and \"CS6\". The property is currently valid only for IPv4, and it is supported only by the \"internal\" DHCP plugin.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_DUID N_("A string containing the DHCPv6 Unique Identifier (DUID) used by the dhcp client to identify itself to DHCPv6 servers (RFC 3315). The DUID is carried in the Client Identifier option. If the property is a hex string ('aa:bb:cc') it is interpreted as a binary DUID and filled as an opaque value in the Client Identifier option. The special value \"lease\" will retrieve the DUID previously used from the lease file belonging to the connection. If no DUID is found and \"dhclient\" is the configured dhcp client, the DUID is searched in the system-wide dhclient lease file. If still no DUID is found, or another dhcp client is used, a global and permanent DUID-UUID (RFC 6355) will be generated based on the machine-id. The special values \"llt\" and \"ll\" will generate a DUID of type LLT or LL (see RFC 3315) based on the current MAC address of the device. In order to try providing a stable DUID-LLT, the time field will contain a constant timestamp that is used globally (for all profiles) and persisted to disk. The special values \"stable-llt\", \"stable-ll\" and \"stable-uuid\" will generate a DUID of the corresponding type, derived from the connection's stable-id and a per-host unique key. You may want to include the \"${DEVICE}\" or \"${MAC}\" specifier in the stable-id, in case this profile gets activated on multiple devices. So, the link-layer address of \"stable-ll\" and \"stable-llt\" will be a generated address derived from the stable id. The DUID-LLT time value in the \"stable-llt\" option will be picked among a static timespan of three years (the upper bound of the interval is the same constant timestamp used in \"llt\"). When the property is unset, the global value provided for \"ipv6.dhcp-duid\" is used. If no global value is provided, the default \"lease\" value is assumed.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME N_("If the \"dhcp-send-hostname\" property is TRUE, then the specified name will be sent to the DHCP server when acquiring a lease. This property and \"dhcp-fqdn\" are mutually exclusive and cannot be set at the same time.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME_FLAGS N_("Flags for the DHCP hostname and FQDN. Currently, this property only includes flags to control the FQDN flags set in the DHCP FQDN option. Supported FQDN flags are \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) and \"fqdn-no-update\" (0x4).  When no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is set, the DHCP FQDN option will contain no flag. Otherwise, if no FQDN flag is set and \"fqdn-clear-flags\" (0x8) is not set, the standard FQDN flags are set in the request: \"fqdn-serv-update\" (0x1), \"fqdn-encoded\" (0x2) for IPv4 and \"fqdn-serv-update\" (0x1) for IPv6. When this property is set to the default value \"none\" (0x0), a global default is looked up in NetworkManager configuration. If that value is unset or also \"none\" (0x0), then the standard FQDN flags described above are sent in the DHCP requests.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_IAID N_("A string containing the \"Identity Association Identifier\" (IAID) used by the DHCP client. The string can be a 32-bit number (either decimal, hexadecimal or as colon separated hexadecimal numbers). Alternatively it can be set to the special values \"mac\", \"perm-mac\", \"ifname\" or \"stable\". When set to \"mac\" (or \"perm-mac\"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to \"ifname\", the IAID is computed by hashing the interface name. The special value \"stable\" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be \"ifname\". For DHCPv4, the IAID is only used with \"ipv4.dhcp-client-id\" values \"duid\" and \"ipv6-duid\" to generate the client-id. For DHCPv6, note that at the moment this property is only supported by the \"internal\" DHCPv6 plugin. The \"dhclient\" DHCPv6 plugin always derives the IAID from the MAC address. The actually used DHCPv6 IAID for a currently activated interface is exposed in the lease information of the device.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_PD_HINT N_("A IPv6 address followed by a slash and a prefix length. If set, the value is sent to the DHCPv6 server as hint indicating the prefix delegation (IA_PD) we want to receive. To only hint a prefix length without prefix, set the address part to the zero address (for example \"::/60\").")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("DNS options for /etc/resolv.conf as described in resolv.conf(5) manual. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"ndots\", \"no-aaaa\", \"no-check-names\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\" and \"inet6\", \"ip6-bytestring\", \"ip6-dotint\", \"no-ip6-dotint\". See the resolv.conf(5) manual. Note that there is a distinction between an unset (default) list and an empty list. In nmcli, to unset the list set the value to \"\". To set an empty list, set it to \" \". Currently, an unset list has the same meaning as an empty list. That might change in the future. The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added. The valid \"ipv4.dns-options\" and \"ipv6.dns-options\" get merged together.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY N_("DNS servers priority. The relative priority for DNS servers specified by this setting.  A lower numerical value is better (higher priority). Negative values have the special effect of excluding other configurations with a greater numerical priority value; so in presence of at least one negative priority, only DNS servers from connections with the lowest priority value will be used. To avoid all DNS leaks, set the priority of the profile that should be used to the most negative value of all active connections profiles. Zero selects a globally configured default value. If the latter is missing or zero too, it defaults to 50 for VPNs (including WireGuard) and 100 for other connections. Note that the priority is to order DNS settings for multiple active connections.  It does not disambiguate multiple DNS servers within the same connection profile. When multiple devices have configurations with the same priority, VPNs will be considered first, then devices with the best (lowest metric) default route and then all other devices. When using dns=default, servers with higher priority will be on top of resolv.conf. To prioritize a given server over another one within the same connection, just specify them in the desired order. Note that commonly the resolver tries name servers in /etc/resolv.conf in the order listed, proceeding with the next server in the list on failure. See for example the \"rotate\" option of the dns-options setting. If there are any negative DNS priorities, then only name servers from the devices with that lowest priority will be considered. When using a DNS resolver that supports Conditional Forwarding or Split DNS (with dns=dnsmasq or dns=systemd-resolved settings), each connection is used to query domains in its search list. The search domains determine which name servers to ask, and the DNS priority is used to prioritize name servers based on the domain.  Queries for domains not present in any search list are routed through connections having the '~.' special wildcard domain, which is added automatically to connections with the default route (or can be added manually).  When multiple connections specify the same domain, the one with the best priority (lowest numerical value) wins.  If a sub domain is configured on another interface it will be accepted regardless the priority, unless parent domain on the other interface has a negative priority, which causes the sub domain to be shadowed. With Split DNS one can avoid undesired DNS leaks by properly configuring DNS priorities and the search domains, so that only name servers of the desired interface are configured.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_SEARCH N_("List of DNS search domains. Domains starting with a tilde ('~') are considered 'routing' domains and are used only to decide the interface over which a query must be forwarded; they are not used to complete unqualified host names. When using a DNS plugin that supports Conditional Forwarding or Split DNS, then the search domains specify which name servers to query. This makes the behavior different from running with plain /etc/resolv.conf. For more information see also the dns-priority setting. When set on a profile that also enabled DHCP, the DNS search list received automatically (option 119 for DHCPv4 and option 24 for DHCPv6) gets merged with the manual list. This can be prevented by setting \"ignore-auto-dns\". Note that if no DNS searches are configured, the fallback will be derived from the domain from DHCP (option 15).")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if \"never-default\" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See \"ip4-auto-default-route\".")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured routes are ignored and only routes specified in the \"routes\" property, if any, are used.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IP6_PRIVACY N_("Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to \"-1\" (unknown) means fallback to global configuration \"ipv6.ip6-privacy\". If also global configuration is unspecified or set to \"-1\", fallback to read \"/proc/sys/net/ipv6/conf/default/use_tempaddr\". Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the \"addr-gen-mode\" property's \"stable-privacy\" setting as another way of avoiding host tracking with IPv6 addresses.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_MAY_FAIL N_("If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_METHOD N_("The IPv6 connection method.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_MTU N_("Maximum transmission unit size, in bytes. If zero (the default), the MTU is set automatically from router advertisements or is left equal to the link-layer MTU. If greater than the link-layer MTU, or greater than zero but less than the minimum IPv6 MTU of 1280, this value has no effect.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_NEVER_DEFAULT N_("If TRUE, this connection will never be the default connection for this IP type, meaning it will never be assigned the default route by NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_RA_TIMEOUT N_("A timeout for waiting Router Advertisements in seconds. If zero (the default), a globally configured default is used. If still unspecified, the timeout depends on the sysctl settings of the device. Set to 2147483647 (MAXINT32) for infinity.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_REPLACE_LOCAL_RULE N_("Connections will default to keep the autogenerated priority 0 local rule unless this setting is set to TRUE.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_REQUIRED_TIMEOUT N_("The minimum time interval in milliseconds for which dynamic IP configuration should be tried before the connection succeeds. This property is useful for example if both IPv4 and IPv6 are enabled and are allowed to fail. Normally the connection succeeds as soon as one of the two address families completes; by setting a required timeout for e.g. IPv4, one can ensure that even if IP6 succeeds earlier than IPv4, NetworkManager waits some time for IPv4 before the connection becomes active. Note that if \"may-fail\" is FALSE for the same address family, this property has no effect as NetworkManager needs to wait for the full DHCP timeout. A zero value means that no required timeout is present, -1 means the default value (either configuration ipvx.required-timeout override or zero).")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_METRIC N_("The default metric for routes that don't explicitly specify a metric. The default value -1 means that the metric is chosen automatically based on the device type. The metric applies to dynamic routes, manual (static) routes that don't have an explicit metric setting, address prefix routes, and the default route. Note that for IPv6, the kernel accepts zero (0) but coerces it to 1024 (user default). Hence, setting this property to zero effectively mean setting it to 1024. For IPv4, zero is a regular value for the metric.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTES N_("Array of IP routes.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_TOKEN N_("Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode. When set, the token is used as IPv6 interface identifier instead of the hardware address. This only applies to addresses from stateless autoconfiguration, not to IPv6 link local addresses.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT N_("How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels. To disable this option, add 0x1 (ip6-ign-encap-limit) to ip-tunnel flags.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLAGS N_("Tunnel flags. Currently, the following values are supported: 0x1 (ip6-ign-encap-limit), 0x2 (ip6-use-orig-tclass), 0x4 (ip6-use-orig-flowlabel), 0x8 (ip6-mip6-dev), 0x10 (ip6-rcv-dscp-copy) and 0x20 (ip6-use-orig-fwmark). They are valid only for IPv6 tunnels.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLOW_LABEL N_("The flow label to assign to tunnel packets. This property applies only to IPv6 tunnels.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FWMARK N_("The fwmark value to assign to tunnel packets. This property can be set to a non zero value only on VTI and VTI6 tunnels.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_INPUT_KEY N_("The key used for tunnel input packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_LOCAL N_("The local endpoint of the tunnel; the value can be empty, otherwise it must contain an IPv4 or IPv6 address.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MODE N_("The tunneling mode. Valid values: ipip (1), gre (2), sit (3), isatap (4), vti (5), ip6ip6 (6), ipip6 (7), ip6gre (8), vti6 (9), gretap (10) and ip6gretap (11)")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_OUTPUT_KEY N_("The key used for tunnel output packets; the property is valid only for certain tunnel modes (GRE, IP6GRE). If empty, no key is used.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_PARENT N_("If given, specifies the parent interface name or parent connection UUID the new device will be bound to so that tunneled packets will only be routed via that interface.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY N_("Whether to enable Path MTU Discovery on this tunnel.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_REMOTE N_("The remote endpoint of the tunnel; the value must contain an IPv4 or IPv6 address.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TOS N_("The type of service (IPv4) or traffic class (IPv6) field to be set on tunneled packets.")
-#define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_TTL N_("The TTL to assign to tunneled packets. 0 is a special value meaning that packets inherit the TTL value.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_ENCRYPT N_("Whether the transmitted traffic must be encrypted.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_MKA_CAK N_("The pre-shared CAK (Connectivity Association Key) for MACsec Key Agreement. Must be a string of 32 hexadecimal characters.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_MKA_CAK_FLAGS N_("Flags indicating how to handle the \"mka-cak\" property.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_MKA_CKN N_("The pre-shared CKN (Connectivity-association Key Name) for MACsec Key Agreement. Must be a string of hexadecimal characters with a even length between 2 and 64.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_MODE N_("Specifies how the CAK (Connectivity Association Key) for MKA (MACsec Key Agreement) is obtained.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_OFFLOAD N_("Specifies the MACsec offload mode. \"off\" (0) disables MACsec offload. \"phy\" (1) and \"mac\" (2) request offload respectively to the PHY or to the MAC; if the selected mode is not available, the connection will fail. \"default\" (-1) uses the global default value specified in NetworkManager configuration; if no global default is defined, the built-in default is \"off\" (0).")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this MACSEC interface should be created.  If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_PORT N_("The port component of the SCI (Secure Channel Identifier), between 1 and 65534.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_SEND_SCI N_("Specifies whether the SCI (Secure Channel Identifier) is included in every packet.")
-#define DESCRIBE_DOC_NM_SETTING_MACSEC_VALIDATION N_("Specifies the validation mode for incoming frames.")
-#define DESCRIBE_DOC_NM_SETTING_MACVLAN_MODE N_("The macvlan mode, which specifies the communication mechanism between multiple macvlans on the same lower device.")
-#define DESCRIBE_DOC_NM_SETTING_MACVLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created.  If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
-#define DESCRIBE_DOC_NM_SETTING_MACVLAN_PROMISCUOUS N_("Whether the interface should be put in promiscuous mode.")
-#define DESCRIBE_DOC_NM_SETTING_MACVLAN_TAP N_("Whether the interface should be a MACVTAP.")
-#define DESCRIBE_DOC_NM_SETTING_MATCH_DRIVER N_("A list of driver names to match. Each element is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern.")
-#define DESCRIBE_DOC_NM_SETTING_MATCH_INTERFACE_NAME N_("A list of interface names to match. Each element is a shell wildcard pattern. An element can be prefixed with a pipe symbol (|) or an ampersand (&). The former means that the element is optional and the latter means that it is mandatory. If there are any optional elements, than the match evaluates to true if at least one of the optional element matches (logical OR). If there are any mandatory elements, then they all must match (logical AND). By default, an element is optional. This means that an element \"foo\" behaves the same as \"|foo\". An element can also be inverted with exclamation mark (!) between the pipe symbol (or the ampersand) and before the pattern. Note that \"!foo\" is a shortcut for the mandatory match \"&!foo\". Finally, a backslash can be used at the beginning of the element (after the optional special characters) to escape the start of the pattern. For example, \"&\\!a\" is an mandatory match for literally \"!a\".")
-#define DESCRIBE_DOC_NM_SETTING_MATCH_KERNEL_COMMAND_LINE N_("A list of kernel command line arguments to match. This may be used to check whether a specific kernel command line option is set (or unset, if prefixed with the exclamation mark). The argument must either be a single word, or an assignment (i.e. two words, joined by \"=\"). In the former case the kernel command line is searched for the word appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is looked for with right and left hand side matching. Wildcard patterns are not supported. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the match.")
-#define DESCRIBE_DOC_NM_SETTING_MATCH_PATH N_("A list of paths to match against the ID_PATH udev property of devices. ID_PATH represents the topological persistent path of a device. It typically contains a subsystem string (pci, usb, platform, etc.) and a subsystem-specific identifier. For PCI devices the path has the form \"pci-$domain:$bus:$device.$function\", where each variable is an hexadecimal value; for example \"pci-0000:0a:00.0\". The path of a device can be obtained with \"udevadm info /sys/class/net/$dev | grep ID_PATH=\" or by looking at the \"path\" property exported by NetworkManager (\"nmcli -f general.path device show $dev\"). Each element of the list is a shell wildcard pattern. See NMSettingMatch:interface-name for how special characters '|', '&', '!' and '\\' are used for optional and mandatory matches and inverting the pattern.")
-#define DESCRIBE_DOC_NM_SETTING_OLPC_MESH_CHANNEL N_("Channel on which the mesh network to join is located.")
-#define DESCRIBE_DOC_NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS N_("Anycast DHCP MAC address used when requesting an IP address via DHCP. The specific anycast address used determines which DHCP server class answers the request. This is currently only implemented by dhclient DHCP plugin.")
-#define DESCRIBE_DOC_NM_SETTING_OLPC_MESH_SSID N_("SSID of the mesh network to join.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_DATAPATH_TYPE N_("The data path type. One of \"system\", \"netdev\" or empty.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_FAIL_MODE N_("The bridge failure mode. One of \"secure\", \"standalone\" or empty.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE N_("Enable or disable multicast snooping.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_RSTP_ENABLE N_("Enable or disable RSTP.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_DEVARGS N_("Open vSwitch DPDK device arguments.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_RXQ N_("Open vSwitch DPDK number of rx queues. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures one queue.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_RXQ_DESC N_("The rx queue size (number of rx descriptors) for DPDK ports. Must be zero or a power of 2 between 1 and 4096, and supported by the hardware. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures 2048 descriptors.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_N_TXQ_DESC N_("The tx queue size (number of tx descriptors) for DPDK ports. Must be zero or a power of 2 between 1 and 4096, and supported by the hardware. Defaults to zero which means to leave the parameter in OVS unspecified and effectively configures 2048 descriptors.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST N_("Open vSwitch openflow port number. Defaults to zero which means that port number will not be specified and it will be chosen randomly by ovs. OpenFlow ports are the network interfaces for passing packets between OpenFlow processing and the rest of the network. OpenFlow switches connect logically to each other via their OpenFlow ports.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_TYPE N_("The interface type. Either \"internal\", \"system\", \"patch\", \"dpdk\", or empty.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_MODE N_("Bonding mode. One of \"active-backup\", \"balance-slb\", or \"balance-tcp\".")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_UPDELAY N_("The time port must be active before it starts forwarding traffic.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_LACP N_("LACP mode. One of \"active\", \"off\", or \"passive\".")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TAG N_("The VLAN tag in the range 0-4095.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_TRUNKS N_("A list of VLAN ranges that this port trunks. The property is valid only for ports with mode \"trunk\", \"native-tagged\", or \"native-untagged port\". If it is empty, the port trunks all VLANs.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_PORT_VLAN_MODE N_("The VLAN mode. One of \"access\", \"native-tagged\", \"native-untagged\", \"trunk\", \"dot1q-tunnel\" or unset.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_BAUD N_("If non-zero, instruct pppd to set the serial port to the specified baudrate.  This value should normally be left as 0 to automatically choose the speed.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_CRTSCTS N_("If TRUE, specify that pppd should set the serial port to use hardware flow control with RTS and CTS signals.  This value should normally be set to FALSE.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_LCP_ECHO_FAILURE N_("If non-zero, instruct pppd to presume the connection to the peer has failed if the specified number of LCP echo-requests go unanswered by the peer.  The \"lcp-echo-interval\" property must also be set to a non-zero value if this property is used.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_LCP_ECHO_INTERVAL N_("If non-zero, instruct pppd to send an LCP echo-request frame to the peer every n seconds (where n is the specified value).  Note that some PPP peers will respond to echo requests and some will not, and it is not possible to autodetect this.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_MPPE_STATEFUL N_("If TRUE, stateful MPPE is used.  See pppd documentation for more information on stateful MPPE.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_MRU N_("If non-zero, instruct pppd to request that the peer send packets no larger than the specified size.  If non-zero, the MRU should be between 128 and 16384.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_MTU N_("If non-zero, instruct pppd to send packets no larger than the specified size.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_NO_VJ_COMP N_("If TRUE, Van Jacobsen TCP header compression will not be requested.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_NOAUTH N_("If TRUE, do not require the other side (usually the PPP server) to authenticate itself to the client.  If FALSE, require authentication from the remote side.  In almost all cases, this should be TRUE.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_NOBSDCOMP N_("If TRUE, BSD compression will not be requested.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_NODEFLATE N_("If TRUE, \"deflate\" compression will not be requested.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REFUSE_CHAP N_("If TRUE, the CHAP authentication method will not be used.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REFUSE_EAP N_("If TRUE, the EAP authentication method will not be used.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REFUSE_MSCHAP N_("If TRUE, the MSCHAP authentication method will not be used.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REFUSE_MSCHAPV2 N_("If TRUE, the MSCHAPv2 authentication method will not be used.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REFUSE_PAP N_("If TRUE, the PAP authentication method will not be used.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REQUIRE_MPPE N_("If TRUE, MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session.  If either 64-bit or 128-bit MPPE is not available the session will fail.  Note that MPPE is not used on mobile broadband connections.")
-#define DESCRIBE_DOC_NM_SETTING_PPP_REQUIRE_MPPE_128 N_("If TRUE, 128-bit MPPE (Microsoft Point-to-Point Encryption) will be required for the PPP session, and the \"require-mppe\" property must also be set to TRUE.  If 128-bit MPPE is not available the session will fail.")
-#define DESCRIBE_DOC_NM_SETTING_PPPOE_PARENT N_("If given, specifies the parent interface name on which this PPPoE connection should be created.  If this property is not specified, the connection is activated on the interface specified in \"interface-name\" of NMSettingConnection.")
-#define DESCRIBE_DOC_NM_SETTING_PPPOE_PASSWORD N_("Password used to authenticate with the PPPoE service.")
-#define DESCRIBE_DOC_NM_SETTING_PPPOE_PASSWORD_FLAGS N_("Flags indicating how to handle the \"password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_PPPOE_SERVICE N_("If specified, instruct PPPoE to only initiate sessions with access concentrators that provide the specified service.  For most providers, this should be left blank.  It is only required if there are multiple access concentrators or a specific service is known to be required.")
-#define DESCRIBE_DOC_NM_SETTING_PPPOE_USERNAME N_("Username used to authenticate with the PPPoE service.")
-#define DESCRIBE_DOC_NM_SETTING_PROXY_BROWSER_ONLY N_("Whether the proxy configuration is for browser only.")
-#define DESCRIBE_DOC_NM_SETTING_PROXY_METHOD N_("Method for proxy configuration, Default is \"none\" (0)")
-#define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_SCRIPT N_("The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes \"file://\" and \"js://\" are supported to explicitly differentiate between the two.")
-#define DESCRIBE_DOC_NM_SETTING_PROXY_PAC_URL N_("PAC URL for obtaining PAC file.")
-#define DESCRIBE_DOC_NM_SETTING_SERIAL_BAUD N_("Speed to use for communication over the serial port.  Note that this value usually has no effect for mobile broadband modems as they generally ignore speed settings and use the highest available speed.")
-#define DESCRIBE_DOC_NM_SETTING_SERIAL_BITS N_("Byte-width of the serial communication. The 8 in \"8n1\" for example.")
-#define DESCRIBE_DOC_NM_SETTING_SERIAL_PARITY N_("Parity setting of the serial port.")
-#define DESCRIBE_DOC_NM_SETTING_SERIAL_SEND_DELAY N_("Time to delay between each byte sent to the modem, in microseconds.")
-#define DESCRIBE_DOC_NM_SETTING_SERIAL_STOPBITS N_("Number of stop bits for communication on the serial port.  Either 1 or 2. The 1 in \"8n1\" for example.")
-#define DESCRIBE_DOC_NM_SETTING_SRIOV_AUTOPROBE_DRIVERS N_("Whether to autoprobe virtual functions by a compatible driver. If set to \"true\" (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to \"false\" (0), VFs will not be claimed and no network interfaces will be created for them. When set to \"default\" (-1), the global default is used; in case the global default is unspecified it is assumed to be \"true\" (1).")
-#define DESCRIBE_DOC_NM_SETTING_SRIOV_ESWITCH_ENCAP_MODE N_("Select the eswitch encapsulation support. Currently it's only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to \"preserve\" (-1) (default) the eswitch encap-mode won't be modified by NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_SRIOV_ESWITCH_INLINE_MODE N_("Select the eswitch inline-mode of the device. Some HWs need the VF driver to put part of the packet headers on the TX descriptor so the e-switch can do proper matching and steering. Currently it's only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to \"preserve\" (-1) (default) the eswitch inline-mode won't be modified by NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_SRIOV_ESWITCH_MODE N_("Select the eswitch mode of the device. Currently it's only supported for PCI PF devices, and only if the eswitch device is managed from the same PCI address than the PF. If set to \"preserve\" (-1) (default) the eswitch mode won't be modified by NetworkManager.")
-#define DESCRIBE_DOC_NM_SETTING_SRIOV_TOTAL_VFS N_("The total number of virtual functions to create. Note that when the sriov setting is present NetworkManager enforces the number of virtual functions on the interface (also when it is zero) during activation and resets it upon deactivation. To prevent any changes to SR-IOV parameters don't add a sriov setting to the connection.")
-#define DESCRIBE_DOC_NM_SETTING_SRIOV_VFS N_("Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: \"INDEX [ATTR=VALUE[ ATTR=VALUE]...]\". for example: \"2 mac=00:11:22:33:44:55 spoof-check=true\". Multiple VFs can be specified using a comma as separator. Currently, the following attributes are supported: mac, spoof-check, trust, min-tx-rate, max-tx-rate, vlans. The \"vlans\" attribute is represented as a semicolon-separated list of VLAN descriptors, where each descriptor has the form \"ID[.PRIORITY[.PROTO]]\". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad.")
-#define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queueing disciplines. When the \"tc\" setting is present, qdiscs from this property are applied upon activation. If the property is empty, all qdiscs are removed and the device will only have the default qdisc assigned by kernel according to the \"net.core.default_qdisc\" sysctl. If the \"tc\" setting is not present, NetworkManager doesn't touch the qdiscs present on the interface.")
-#define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS N_("Array of TC traffic filters. When the \"tc\" setting is present, filters from this property are applied upon activation. If the property is empty, NetworkManager removes all the filters. If the \"tc\" setting is not present, NetworkManager doesn't touch the filters present on the interface.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_CONFIG N_("The JSON configuration for the team network interface.  The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used.  See man teamd.conf for the format details.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_COUNT N_("Corresponds to the teamd mcast_rejoin.count.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL N_("Corresponds to the teamd mcast_rejoin.interval.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_NOTIFY_PEERS_COUNT N_("Corresponds to the teamd notify_peers.count.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL N_("Corresponds to the teamd notify_peers.interval.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER N_("Corresponds to the teamd runner.name. Permitted values are: \"roundrobin\", \"broadcast\", \"activebackup\", \"loadbalance\", \"lacp\", \"random\".")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_ACTIVE N_("Corresponds to the teamd runner.active.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY N_("Corresponds to the teamd runner.agg_select_policy.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_FAST_RATE N_("Corresponds to the teamd runner.fast_rate.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_HWADDR_POLICY N_("Corresponds to the teamd runner.hwaddr_policy.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_MIN_PORTS N_("Corresponds to the teamd runner.min_ports.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_SYS_PRIO N_("Corresponds to the teamd runner.sys_prio.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_TX_BALANCER N_("Corresponds to the teamd runner.tx_balancer.name.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL N_("Corresponds to the teamd runner.tx_balancer.interval.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_RUNNER_TX_HASH N_("Corresponds to the teamd runner.tx_hash.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_CONFIG N_("The JSON configuration for the team port. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_KEY N_("Corresponds to the teamd ports.PORTIFNAME.lacp_key.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LACP_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.lacp_prio.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-inactive', 'send-always'. See teamd.conf man for more details.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_PRIO N_("Corresponds to the teamd ports.PORTIFNAME.prio.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_QUEUE_ID N_("Corresponds to the teamd ports.PORTIFNAME.queue_id. When set to -1 means the parameter is skipped from the json config.")
-#define DESCRIBE_DOC_NM_SETTING_TEAM_PORT_STICKY N_("Corresponds to the teamd ports.PORTIFNAME.sticky.")
-#define DESCRIBE_DOC_NM_SETTING_TUN_GROUP N_("The group ID which will own the device. If set to NULL everyone will be able to use the device.")
-#define DESCRIBE_DOC_NM_SETTING_TUN_MODE N_("The operating mode of the virtual device. Allowed values are \"tun\" (1) to create a layer 3 device and \"tap\" (2) to create an Ethernet-like layer 2 one.")
-#define DESCRIBE_DOC_NM_SETTING_TUN_MULTI_QUEUE N_("If the property is set to TRUE, the interface will support multiple file descriptors (queues) to parallelize packet sending or receiving. Otherwise, the interface will only support a single queue.")
-#define DESCRIBE_DOC_NM_SETTING_TUN_OWNER N_("The user ID which will own the device. If set to NULL everyone will be able to use the device.")
-#define DESCRIBE_DOC_NM_SETTING_TUN_PI N_("If TRUE the interface will prepend a 4 byte header describing the physical interface to the packets.")
-#define DESCRIBE_DOC_NM_SETTING_TUN_VNET_HDR N_("If TRUE the IFF_VNET_HDR the tunnel packets will include a virtio network header.")
-#define DESCRIBE_DOC_NM_SETTING_USER_DATA N_("A dictionary of key/value pairs with user data. This data is ignored by NetworkManager and can be used at the users discretion. The keys only support a strict ascii format, but the values can be arbitrary UTF8 strings up to a certain length.")
-#define DESCRIBE_DOC_NM_SETTING_VLAN_EGRESS_PRIORITY_MAP N_("For outgoing packets, a list of mappings from Linux SKB priorities to 802.1p priorities.  The mapping is given in the format \"from:to\" where both \"from\" and \"to\" are unsigned integers, ie \"7:3\".")
-#define DESCRIBE_DOC_NM_SETTING_VLAN_FLAGS N_("One or more flags which control the behavior and features of the VLAN interface.  Flags include \"reorder-headers\" (0x1) (reordering of output packet headers), \"gvrp\" (0x2) (use of the GVRP protocol), and \"loose-binding\" (0x4) (loose binding of the interface to its master device's operating state). \"mvrp\" (0x8) (use of the MVRP protocol). The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS, but it used to be 0. To preserve backward compatibility, the default-value in the D-Bus API continues to be 0 and a missing property on D-Bus is still considered as 0.")
-#define DESCRIBE_DOC_NM_SETTING_VLAN_ID N_("The VLAN identifier that the interface created by this connection should be assigned. The valid range is from 0 to 4094, without the reserved id 4095.")
-#define DESCRIBE_DOC_NM_SETTING_VLAN_INGRESS_PRIORITY_MAP N_("For incoming packets, a list of mappings from 802.1p priorities to Linux SKB priorities.  The mapping is given in the format \"from:to\" where both \"from\" and \"to\" are unsigned integers, ie \"7:3\".")
-#define DESCRIBE_DOC_NM_SETTING_VLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this VLAN interface should be created.  If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.")
-#define DESCRIBE_DOC_NM_SETTING_VLAN_PROTOCOL N_("Specifies the VLAN protocol to use for encapsulation. Supported values are: '802.1Q', '802.1ad'. If not specified the default value is '802.1Q'.")
-#define DESCRIBE_DOC_NM_SETTING_VPN_DATA N_("Dictionary of key/value pairs of VPN plugin specific data.  Both keys and values must be strings.")
-#define DESCRIBE_DOC_NM_SETTING_VPN_PERSISTENT N_("If the VPN service supports persistence, and this property is TRUE, the VPN will attempt to stay connected across link changes and outages, until explicitly disconnected.")
-#define DESCRIBE_DOC_NM_SETTING_VPN_SECRETS N_("Dictionary of key/value pairs of VPN plugin specific secrets like passwords or private keys.  Both keys and values must be strings.")
-#define DESCRIBE_DOC_NM_SETTING_VPN_SERVICE_TYPE N_("D-Bus service name of the VPN plugin that this setting uses to connect to its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc plugin.")
-#define DESCRIBE_DOC_NM_SETTING_VPN_TIMEOUT N_("Timeout for the VPN service to establish the connection. Some services may take quite a long time to connect. Value of 0 means a default timeout, which is 60 seconds (unless overridden by vpn.timeout in configuration file). Values greater than zero mean timeout in seconds.")
-#define DESCRIBE_DOC_NM_SETTING_VPN_USER_NAME N_("If the VPN connection requires a user name for authentication, that name should be provided here.  If the connection is available to more than one user, and the VPN requires each user to supply a different name, then leave this property empty.  If this property is empty, NetworkManager will automatically supply the username of the user which requested the VPN connection.")
-#define DESCRIBE_DOC_NM_SETTING_VRF_TABLE N_("The routing table for this VRF.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_AGEING N_("Specifies the lifetime in seconds of FDB entries learnt by the kernel.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_DESTINATION_PORT N_("Specifies the UDP destination port to communicate to the remote VXLAN tunnel endpoint.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_ID N_("Specifies the VXLAN Network Identifier (or VXLAN Segment Identifier) to use.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_L2_MISS N_("Specifies whether netlink LL ADDR miss notifications are generated.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_L3_MISS N_("Specifies whether netlink IP ADDR miss notifications are generated.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_LEARNING N_("Specifies whether unknown source link layer addresses and IP addresses are entered into the VXLAN device forwarding database.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_LIMIT N_("Specifies the maximum number of FDB entries. A value of zero means that the kernel will store unlimited entries.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_LOCAL N_("If given, specifies the source IP address to use in outgoing packets.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_PROXY N_("Specifies whether ARP proxy is turned on.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_REMOTE N_("Specifies the unicast destination IP address to use in outgoing packets when the destination link layer address is not known in the VXLAN device forwarding database, or the multicast IP address to join.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_RSC N_("Specifies whether route short circuit is turned on.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_SOURCE_PORT_MAX N_("Specifies the maximum UDP source port to communicate to the remote VXLAN tunnel endpoint.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_SOURCE_PORT_MIN N_("Specifies the minimum UDP source port to communicate to the remote VXLAN tunnel endpoint.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_TOS N_("Specifies the TOS value to use in outgoing packets.")
-#define DESCRIBE_DOC_NM_SETTING_VXLAN_TTL N_("Specifies the time-to-live value to use in outgoing packets.")
-#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_PEER N_("The P2P device that should be connected to. Currently, this is the only way to create or join a group.")
-#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WFD_IES N_("The Wi-Fi Display (WFD) Information Elements (IEs) to set. Wi-Fi Display requires a protocol specific information element to be set in certain Wi-Fi frames. These can be specified here for the purpose of establishing a connection. This setting is only useful when implementing a Wi-Fi Display client.")
-#define DESCRIBE_DOC_NM_SETTING_WIFI_P2P_WPS_METHOD N_("Flags indicating which mode of WPS is to be used. There's little point in changing the default setting as NetworkManager will automatically determine the best method to use.")
-#define DESCRIBE_DOC_NM_SETTING_WIMAX_MAC_ADDRESS N_("If specified, this connection will only apply to the WiMAX device whose MAC address matches. This property does not change the MAC address of the device (known as MAC spoofing).")
-#define DESCRIBE_DOC_NM_SETTING_WIMAX_NETWORK_NAME N_("Network Service Provider (NSP) name of the WiMAX network this connection should use.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_ACCEPT_ALL_MAC_ADDRESSES N_("When TRUE, setup the interface to accept packets for all MAC addresses. This is enabling the kernel interface flag IFF_PROMISC. When FALSE, the interface will only accept the packets with the interface destination mac address or broadcast.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_AUTO_NEGOTIATE N_("When TRUE, enforce auto-negotiation of speed and duplex mode. If \"speed\" and \"duplex\" properties are both specified, only that single mode will be advertised and accepted during the link auto-negotiation process: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. When FALSE, \"speed\" and \"duplex\" properties should be both set or link configuration will be skipped.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_CLONED_MAC_ADDRESS N_("If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values \"preserve\", \"permanent\", \"random\" and \"stable\" are supported. \"preserve\" means not to touch the MAC address on activation. \"permanent\" means to use the permanent hardware address if the device has one (otherwise this is treated as \"preserve\"). \"random\" creates a random MAC address on each connect. \"stable\" creates a hashed MAC address based on connection.stable-id and a machine dependent key. If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to \"preserve\" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as \"assigned-mac-address\" or the deprecated \"cloned-mac-address\".")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_DUPLEX N_("When a value is set, either \"half\" or \"full\", configures the device to use the specified duplex mode. If \"auto-negotiate\" is \"yes\" the specified duplex mode will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabits modes, as in these cases link negotiation is mandatory. If the value is unset (the default), the link configuration will be either skipped (if \"auto-negotiate\" is \"no\", the default) or will be auto-negotiated (if \"auto-negotiate\" is \"yes\") and the local device will advertise all the supported duplex modes. Must be set together with the \"speed\" property if specified. Before specifying a duplex mode be sure your device supports it.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK N_("With \"cloned-mac-address\" setting \"random\" or \"stable\", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting \"FE:FF:FF:00:00:00\" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the \"random\" or \"stable\" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of \"FE:FF:FF:00:00:00 68:F7:28:00:00:00\" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of \"02:00:00:00:00:00 00:00:00:00:00:00\" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, \"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00\" will create a fully scrambled MAC address, randomly locally or globally administered.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_MAC_ADDRESS N_("If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST N_("If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55).")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_PORT N_("Specific port type to use if the device supports multiple attachment methods.  One of \"tp\" (Twisted Pair), \"aui\" (Attachment Unit Interface), \"bnc\" (Thin Ethernet) or \"mii\" (Media Independent Interface). If the device supports only one port type, this setting is ignored.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_S390_NETTYPE N_("s390 network device type; one of \"qeth\", \"lcs\", or \"ctc\", representing the different types of virtual network devices available on s390 systems.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_S390_OPTIONS N_("Dictionary of key/value pairs of s390-specific device options.  Both keys and values must be strings.  Allowed keys include \"portno\", \"layer2\", \"portname\", \"protocol\", among others.  Key names must contain only alphanumeric characters (ie, [a-zA-Z0-9]). Currently, NetworkManager itself does nothing with this information. However, s390utils ships a udev rule which parses this information and applies it to the interface.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_S390_SUBCHANNELS N_("Identifies specific subchannels that this network device uses for communication with z/VM or s390 host.  Like the \"mac-address\" property for non-z/VM devices, this property can be used to ensure this connection only applies to the network device that uses these subchannels.  The list should contain exactly 3 strings, and each string may only be composed of hexadecimal characters and the period (.) character.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_SPEED N_("When a value greater than 0 is set, configures the device to use the specified speed. If \"auto-negotiate\" is \"yes\" the specified speed will be the only one advertised during link negotiation: this works only for BASE-T 802.3 specifications and is useful for enforcing gigabit speeds, as in this case link negotiation is mandatory. If the value is unset (0, the default), the link configuration will be either skipped (if \"auto-negotiate\" is \"no\", the default) or will be auto-negotiated (if \"auto-negotiate\" is \"yes\") and the local device will advertise all the supported speeds. In Mbit/s, ie 100 == 100Mbit/s. Must be set together with the \"duplex\" property when non-zero. Before specifying a speed value be sure your device supports it.")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_WAKE_ON_LAN N_("The NMSettingWiredWakeOnLan options to enable. Not all devices support all options. May be any combination of \"phy\" (0x2), \"unicast\" (0x4), \"multicast\" (0x8), \"broadcast\" (0x10), \"arp\" (0x20), \"magic\" (0x40) or the special values \"default\" (0x1) (to use global settings) and \"ignore\" (0x8000) (to disable management of Wake-on-LAN in NetworkManager).")
-#define DESCRIBE_DOC_NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD N_("If specified, the password used with magic-packet-based Wake-on-LAN, represented as an Ethernet MAC address.  If NULL, no password will be required.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_FWMARK N_("The use of fwmark is optional and is by default off. Setting it to 0 disables it. Otherwise, it is a 32-bit fwmark for outgoing packets. Note that \"ip4-auto-default-route\" or \"ip6-auto-default-route\" enabled, implies to automatically choose a fwmark.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE N_("Whether to enable special handling of the IPv4 default route. If enabled, the IPv4 default route from wireguard.peer-routes will be placed to a dedicated routing-table and two policy routing rules will be added. The fwmark number is also used as routing-table for the default-route, and if fwmark is zero, an unused fwmark/table is chosen automatically. This corresponds to what wg-quick does with Table=auto and what WireGuard calls \"Improved Rule-based Routing\". Note that for this automatism to work, you usually don't want to set ipv4.gateway, because that will result in a conflicting default route. Leaving this at the default will enable this option automatically if ipv4.never-default is not set and there are any peers that use a default-route as allowed-ips. Since this automatism only makes sense if you also have a peer with an /0 allowed-ips, it is usually not necessary to enable this explicitly. However, you can disable it if you want to configure your own routing and rules.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE N_("Like ip4-auto-default-route, but for the IPv6 default route.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_LISTEN_PORT N_("The listen-port. If listen-port is not specified, the port will be chosen randomly when the interface comes up.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple fragments. If zero a default MTU is used. Note that contrary to wg-quick's MTU setting, this does not take into account the current routes at the time of activation.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PEER_ROUTES N_("Whether to automatically add routes for the AllowedIPs ranges of the peers. If TRUE (the default), NetworkManager will automatically add routes in the routing tables according to ipv4.route-table and ipv6.route-table. Usually you want this automatism enabled. If FALSE, no such routes are added automatically. In this case, the user may want to configure static routes in ipv4.routes and ipv6.routes, respectively. Note that if the peer's AllowedIPs is \"0.0.0.0/0\" or \"::/0\" and the profile's ipv4.never-default or ipv6.never-default setting is enabled, the peer route for this peer won't be added automatically.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY N_("The 256 bit private-key in base64 encoding.")
-#define DESCRIBE_DOC_NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS N_("Flags indicating how to handle the \"private-key\" property.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_AP_ISOLATION N_("Configures AP isolation, which prevents communication between wireless devices connected to this AP. This property can be set to a value different from \"default\" (-1) only when the interface is configured in AP mode. If set to \"true\" (1), devices are not able to communicate with each other. This increases security because it protects devices against attacks from other clients in the network. At the same time, it prevents devices to access resources on the same wireless networks as file shares, printers, etc. If set to \"false\" (0), devices can talk to each other. When set to \"default\" (-1), the global default is used; in case the global default is unspecified it is assumed to be \"false\" (0).")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_BAND N_("802.11 frequency band of the network.  One of \"a\" for 5GHz 802.11a or \"bg\" for 2.4GHz 802.11.  This will lock associations to the Wi-Fi network to the specific band, i.e. if \"a\" is specified, the device will not associate with the same network in the 2.4GHz band even if the network's settings are compatible.  This setting depends on specific driver capability and may not work with all drivers.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_BSSID N_("If specified, directs the device to only associate with the given access point.  This capability is highly driver dependent and not supported by all devices.  Note: this property does not control the BSSID used when creating an Ad-Hoc network and is unlikely to in the future. Locking a client profile to a certain BSSID will prevent roaming and also disable background scanning. That can be useful, if there is only one access point for the SSID.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_CHANNEL N_("Wireless channel to use for the Wi-Fi connection.  The device will only join (or create for Ad-Hoc networks) a Wi-Fi network on the specified channel.  Because channel numbers overlap between bands, this property also requires the \"band\" property to be set.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS N_("If specified, request that the device use this MAC address instead. This is known as MAC cloning or spoofing. Beside explicitly specifying a MAC address, the special values \"preserve\", \"permanent\", \"random\", \"stable\" and \"stable-ssid\" are supported. \"preserve\" means not to touch the MAC address on activation. \"permanent\" means to use the permanent hardware address of the device. \"random\" creates a random MAC address on each connect. \"stable\" creates a hashed MAC address based on connection.stable-id and a machine dependent key. \"stable-ssid\" creates a hashed MAC address based on the SSID, the same as setting the stable-id to \"${NETWORK_SSID}\". If unspecified, the value can be overwritten via global defaults, see manual of NetworkManager.conf. If still unspecified, it defaults to \"preserve\" (older versions of NetworkManager may use a different default value). On D-Bus, this field is expressed as \"assigned-mac-address\" or the deprecated \"cloned-mac-address\".")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK N_("With \"cloned-mac-address\" setting \"random\" or \"stable\", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting \"FE:FF:FF:00:00:00\" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the \"random\" or \"stable\" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of \"FE:FF:FF:00:00:00 68:F7:28:00:00:00\" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of \"02:00:00:00:00:00 00:00:00:00:00:00\" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, \"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00\" will create a fully scrambled MAC address, randomly locally or globally administered.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_HIDDEN N_("If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID.  However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS N_("If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST N_("A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\").")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION N_("One of \"default\" (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  \"never\" (1) (never randomize the MAC address), or \"always\" (2) (always randomize the MAC address).")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MODE N_("Wi-Fi network mode; one of \"infrastructure\", \"mesh\", \"adhoc\" or \"ap\".  If blank, infrastructure is assumed.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_POWERSAVE N_("One of \"disable\" (2) (disable Wi-Fi power saving), \"enable\" (3) (enable Wi-Fi power saving), \"ignore\" (1) (don't touch currently configure setting) or \"default\" (0) (use the globally configured value). All other values are reserved.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_RATE N_("This property is not implemented and has no effect.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SEEN_BSSIDS N_("A list of BSSIDs (each BSSID formatted as a MAC address like \"00:11:22:33:44:55\") that have been detected as part of the Wi-Fi network.  NetworkManager internally tracks previously seen BSSIDs. The property is only meant for reading and reflects the BSSID list of NetworkManager. The changes you make to this property will not be preserved. This is not a regular property that the user would configure. Instead, NetworkManager automatically sets the seen BSSIDs and tracks them internally in \"/var/lib/NetworkManager/seen-bssids\" file.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SSID N_("SSID of the Wi-Fi network. Must be specified.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_TX_POWER N_("This property is not implemented and has no effect.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_WAKE_ON_WLAN N_("The NMSettingWirelessWakeOnWLan options to enable. Not all devices support all options. May be any combination of \"any\" (0x2), \"disconnect\" (0x4), \"magic\" (0x8), \"gtk-rekey-failure\" (0x10), \"eap-identity-request\" (0x20), \"4way-handshake\" (0x40), \"rfkill-release\" (0x80), \"tcp\" (0x100) or the special values \"default\" (0x1) (to use global settings) and \"ignore\" (0x8000) (to disable management of Wake-on-LAN in NetworkManager).")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_AUTH_ALG N_("When WEP is used (ie, key-mgmt = \"none\" or \"ieee8021x\") indicate the 802.11 authentication algorithm required by the AP here.  One of \"open\" for Open System, \"shared\" for Shared Key, or \"leap\" for Cisco LEAP.  When using Cisco LEAP (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\") the \"leap-username\" and \"leap-password\" properties must be specified.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_FILS N_("Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for the connection.  One of \"default\" (0) (use global default value), \"disable\" (1) (disable FILS), \"optional\" (2) (enable FILS if the supplicant and the access point support it) or \"required\" (3) (enable FILS and fail if not supported).  When set to \"default\" (0) and no global default is set, FILS will be optionally enabled.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_GROUP N_("A list of group/broadcast encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list.  For maximum compatibility leave this property empty.  Each list element may be one of \"wep40\", \"wep104\", \"tkip\", or \"ccmp\".")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_KEY_MGMT N_("Key management used for the connection. One of \"none\" (WEP or no password protection), \"ieee8021x\" (Dynamic WEP), \"owe\" (Opportunistic Wireless Encryption), \"wpa-psk\" (WPA2 + WPA3 personal), \"sae\" (WPA3 personal only), \"wpa-eap\" (WPA2 + WPA3 enterprise) or \"wpa-eap-suite-b-192\" (WPA3 enterprise only). This property must be set for any Wi-Fi connection that uses security.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD N_("The login password for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\").")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS N_("Flags indicating how to handle the \"leap-password\" property.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME N_("The login username for legacy LEAP connections (ie, key-mgmt = \"ieee8021x\" and auth-alg = \"leap\").")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PAIRWISE N_("A list of pairwise encryption algorithms which prevents connections to Wi-Fi networks that do not utilize one of the algorithms in the list. For maximum compatibility leave this property empty.  Each list element may be one of \"tkip\" or \"ccmp\".")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PMF N_("Indicates whether Protected Management Frames (802.11w) must be enabled for the connection.  One of \"default\" (0) (use global default value), \"disable\" (1) (disable PMF), \"optional\" (2) (enable PMF if the supplicant and the access point support it) or \"required\" (3) (enable PMF and fail if not supported).  When set to \"default\" (0) and no global default is set, PMF will be optionally enabled.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PROTO N_("List of strings specifying the allowed WPA protocol versions to use. Each element may be one \"wpa\" (allow WPA) or \"rsn\" (allow WPA2/RSN).  If not specified, both WPA and RSN connections are allowed.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK N_("Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII passphrase of 8 to 63 characters that is (as specified in the 802.11i standard) hashed to derive the actual key, or the key in form of 64 hexadecimal character. The WPA3-Personal networks use a passphrase of any length for SAE authentication.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS N_("Flags indicating how to handle the \"psk\" property.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS N_("Flags indicating how to handle the \"wep-key0\", \"wep-key1\", \"wep-key2\", and \"wep-key3\" properties.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE N_("Controls the interpretation of WEP keys.  Allowed values are \"key\" (1), in which case the key is either a 10- or 26-character hexadecimal string, or a 5- or 13-character ASCII password; or \"passphrase\" (2), in which case the passphrase is provided as a string and will be hashed using the de-facto MD5 method to derive the actual WEP key.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY0 N_("Index 0 WEP key.  This is the WEP key used in most networks.  See the \"wep-key-type\" property for a description of how this key is interpreted.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY1 N_("Index 1 WEP key.  This WEP index is not used by most networks.  See the \"wep-key-type\" property for a description of how this key is interpreted.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY2 N_("Index 2 WEP key.  This WEP index is not used by most networks.  See the \"wep-key-type\" property for a description of how this key is interpreted.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_KEY3 N_("Index 3 WEP key.  This WEP index is not used by most networks.  See the \"wep-key-type\" property for a description of how this key is interpreted.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX N_("When static WEP is used (ie, key-mgmt = \"none\") and a non-default WEP key index is used by the AP, put that WEP key index here.  Valid values are 0 (default key) through 3.  Note that some consumer access points (like the Linksys WRT54G) number the keys 1 - 4.")
-#define DESCRIBE_DOC_NM_SETTING_WIRELESS_SECURITY_WPS_METHOD N_("Flags indicating which mode of WPS is to be used if any. There's little point in changing the default setting as NetworkManager will automatically determine whether it's feasible to start WPS enrollment from the Access Point capabilities. WPS can be disabled by setting this property to a value of 1.")
-#define DESCRIBE_DOC_NM_SETTING_WPAN_CHANNEL N_("IEEE 802.15.4 channel. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
-#define DESCRIBE_DOC_NM_SETTING_WPAN_MAC_ADDRESS N_("If specified, this connection will only apply to the IEEE 802.15.4 (WPAN) MAC layer device whose permanent MAC address matches.")
-#define DESCRIBE_DOC_NM_SETTING_WPAN_PAGE N_("IEEE 802.15.4 channel page. A positive integer or -1, meaning \"do not set, use whatever the device is already set to\".")
-#define DESCRIBE_DOC_NM_SETTING_WPAN_PAN_ID N_("IEEE 802.15.4 Personal Area Network (PAN) identifier.")
-#define DESCRIBE_DOC_NM_SETTING_WPAN_SHORT_ADDRESS N_("Short IEEE 802.15.4 address to be used within a restricted environment.")
-#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_PRIO N_("The port priority for bond active port re-selection during failover. A higher number means a higher priority in selection. The primary port has the highest priority. This option is only compatible with active-backup, balance-tlb and balance-alb modes.")
-#define DESCRIBE_DOC_NM_SETTING_BOND_PORT_QUEUE_ID N_("The queue ID of this bond port. The maximum value of queue ID is the number of TX queues currently active in device.")
-#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DHCP N_("Whether the system hostname can be determined from DHCP on this connection. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"true\" (1).")
-#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP N_("Whether the system hostname can be determined from reverse DNS lookup of addresses on this device. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"true\" (1).")
-#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT N_("If set to \"true\" (1), NetworkManager attempts to get the hostname via DHCPv4/DHCPv6 or reverse DNS lookup on this device only when the device has the default route for the given address family (IPv4/IPv6). If set to \"false\" (0), the hostname can be set from this device even if it doesn't have the default route. When set to \"default\" (-1), the value from global configuration is used. If the property doesn't have a value in the global configuration, NetworkManager assumes the value to be \"false\" (0).")
-#define DESCRIBE_DOC_NM_SETTING_HOSTNAME_PRIORITY N_("The relative priority of this connection to determine the system hostname. A lower numerical value is better (higher priority).  A connection with higher priority is considered before connections with lower priority. If the value is zero, it can be overridden by a global value from NetworkManager configuration. If the property doesn't have a value in the global configuration, the value is assumed to be 100. Negative values have the special effect of excluding other connections with a greater numerical priority value; so in presence of at least one negative priority, only connections with the lowest priority value will be used to determine the hostname.")
-#define DESCRIBE_DOC_NM_SETTING_HSR_MULTICAST_SPEC N_("The last byte of supervision address.")
-#define DESCRIBE_DOC_NM_SETTING_HSR_PORT1 N_("The port1 interface name of the HSR. This property is mandatory.")
-#define DESCRIBE_DOC_NM_SETTING_HSR_PORT2 N_("The port2 interface name of the HSR. This property is mandatory.")
-#define DESCRIBE_DOC_NM_SETTING_HSR_PRP N_("The protocol used by the interface, whether it is PRP or HSR.")
-#define DESCRIBE_DOC_NM_SETTING_LINK_GRO_MAX_SIZE N_("The maximum size of a packet built by the Generic Receive Offload stack for this device. The value must be between 0 and 4294967295. When set to -1, the existing value is preserved.")
-#define DESCRIBE_DOC_NM_SETTING_LINK_GSO_MAX_SEGMENTS N_("The maximum segments of a Generic Segment Offload packet the device should accept. The value must be between 0 and 4294967295. When set to -1, the existing value is preserved.")
-#define DESCRIBE_DOC_NM_SETTING_LINK_GSO_MAX_SIZE N_("The maximum size of a Generic Segment Offload packet the device should accept. The value must be between 0 and 4294967295. When set to -1, the existing value is preserved.")
-#define DESCRIBE_DOC_NM_SETTING_LINK_TX_QUEUE_LENGTH N_("The size of the transmit queue for the device, in number of packets. The value must be between 0 and 4294967295. When set to -1, the existing value is preserved.")
-#define DESCRIBE_DOC_NM_SETTING_LOOPBACK_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with external-ids for OVS.")
-#define DESCRIBE_DOC_NM_SETTING_OVS_OTHER_CONFIG_DATA N_("A dictionary of key/value pairs with other_config settings for OVS. See also \"other_config\" in the \"ovs-vswitchd.conf.db\" manual for the keys that OVS supports.")
-#define DESCRIBE_DOC_NM_SETTING_VETH_PEER N_("This property specifies the peer interface name of the veth. This property is mandatory.")
diff --git a/src/libnmc-setting/settings-docs.h.in b/src/libnmc-setting/settings-docs.h.in
index c4014166..fc5299fc 100644
--- a/src/libnmc-setting/settings-docs.h.in
+++ b/src/libnmc-setting/settings-docs.h.in
@@ -5,16 +5,17 @@
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_PORTS N_("Whether or not ports of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for controller connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave port connections untouched, 1: activate all the port connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-ports is read to determine the real value. If it is default as well, this fallbacks to 0.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY N_("The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES N_("The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES N_("Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES N_("Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0. Deprecated 1.46. Use \"autoconnect-ports\" instead, this is just an alias.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_CONTROLLER N_("Interface name of the controller device or UUID of the controller connection.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_DNS_OVER_TLS N_("Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: \"yes\" (2) use DNSOverTls and disabled fallback, \"opportunistic\" (1) use DNSOverTls but allow fallback to unencrypted resolution, \"no\" (0) don't ever use DNSOverTls. If unspecified \"default\" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_DOWN_ON_POWEROFF N_("Whether the connection will be brought down before the system is powered off.  The default value is \"default\" (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as \"no\" (0).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT N_("If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_ID N_("A human readable unique identifier for the connection, like \"Work Wi-Fi\" or \"T-Mobile 3G\".")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_INTERFACE_NAME N_("The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_LLDP N_("Whether LLDP is enabled for the connection.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_LLMNR N_("Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: \"yes\" (2) register hostname and resolving for the connection, \"no\" (0) disable LLMNR for the interface, \"resolve\" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, \"default\" ultimately depends on the DNS plugin (which for systemd-resolved currently means \"yes\"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_MASTER N_("Interface name of the master device or UUID of the master connection. Deprecated 1.46. Use \"controller\" instead, this is just an alias.")
-#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MDNS N_("Whether mDNS is enabled for the connection. The permitted values are: \"yes\" (2) register hostname and resolving for the connection, \"no\" (0) disable mDNS for the interface, \"resolve\" (1) do not register hostname but allow resolving of mDNS host names and \"default\" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, \"default\" ultimately depends on the DNS plugin (which for systemd-resolved currently means \"no\"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
+#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MDNS N_("Whether mDNS is enabled for the connection. The permitted values are: \"yes\" (2) register hostname and resolving for the connection, \"no\" (0) disable mDNS for the interface, \"resolve\" (1) do not register hostname but allow resolving of mDNS host names and \"default\" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, \"default\" ultimately depends on the DNS plugin. This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. Currently the only supported DNS plugin is systemd-resolved. For systemd-resolved, the default is configurable via MulticastDNS= setting in resolved.conf.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_METERED N_("Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately.")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_MPTCP_FLAGS N_("Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. If \"disabled\" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. The \"enabled\" (0x2) flag means that MPTCP handling is enabled. This flag can also be implied from the presence of other flags. Even when enabled, MPTCP handling will by default still be disabled unless \"/proc/sys/net/mptcp/enabled\" sysctl is on. NetworkManager does not change the sysctl and this is up to the administrator or distribution. To configure endpoints even if the sysctl is disabled, \"also-without-sysctl\" (0x4) flag can be used. In that case, NetworkManager doesn't look at the sysctl and configures endpoints regardless. Even when enabled, NetworkManager will only configure MPTCP endpoints for a certain address family, if there is a unicast default route (0.0.0.0/0 or ::/0) in the main routing table. The flag \"also-without-default-route\" (0x8) can override that. When MPTCP handling is enabled then endpoints are configured with the specified address flags \"signal\" (0x10), \"subflow\" (0x20), \"backup\" (0x40), \"fullmesh\" (0x80). See ip-mptcp(8) manual for additional information about the flags. If the flags are zero (0x0), the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is \"enabled,subflow\". Note that this means that MPTCP is by default done depending on the \"/proc/sys/net/mptcp/enabled\" sysctl. NetworkManager does not change the MPTCP limits nor enable MPTCP via \"/proc/sys/net/mptcp/enabled\". That is a host configuration which the admin can change via sysctl and ip-mptcp. Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2).")
 #define DESCRIBE_DOC_NM_SETTING_CONNECTION_MUD_URL N_("If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with \"https://\". The special value \"none\" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is \"none\".")
@@ -35,17 +36,18 @@
 #define DESCRIBE_DOC_NM_SETTING_802_1X_ALTSUBJECT_MATCHES N_("List of strings to be matched against the altSubjectName of the certificate presented by the authentication server. If the list is empty, no verification of the server certificate's altSubjectName is performed.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_ANONYMOUS_IDENTITY N_("Anonymous identity string for EAP authentication methods.  Used as the unencrypted identity with EAP types that support different tunneled identity like EAP-TTLS.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_AUTH_TIMEOUT N_("A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT N_("Contains the CA certificate if used by the EAP method specified in the \"eap\" property. Certificate data is specified using a \"scheme\"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT N_("Contains the path to the CA certificate if used by the EAP method specified in the 802-1x.eap property. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling 802-1x.system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT_PASSWORD N_("The password used to access the CA certificate stored in \"ca-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"ca-cert-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_CA_PATH N_("UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the \"ca-cert\" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT N_("Contains the client certificate if used by the EAP method specified in the \"eap\" property. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT N_("Contains the path to the client certificate if used by the EAP method specified in the 802-1x.eap property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT_PASSWORD N_("The password used to access the client certificate stored in \"client-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"client-cert-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_DOMAIN_MATCH N_("Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a \";\" delimited list.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH N_("Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a \";\" delimited list.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_EAP N_("The allowed EAP method to be used when authenticating to the network with 802.1x.  Valid methods are: \"leap\", \"md5\", \"tls\", \"peap\", \"ttls\", \"pwd\", and \"fast\".  Each method requires different configuration using the properties of this setting; refer to wpa_supplicant documentation for the allowed combinations.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_IDENTITY N_("Identity string for EAP authentication methods.  Often the user's user or login name.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_OPENSSL_CIPHERS N_("Define openssl_ciphers for wpa_supplicant. Openssl sometimes moves ciphers among SECLEVELs, thus compiled-in default value in wpa_supplicant (as modified by some linux distributions) sometimes prevents to connect to old servers that do not support new protocols.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_OPTIONAL N_("Whether the 802.1X authentication is optional. If TRUE, the activation will continue even after a timeout or an authentication failure. Setting the property to TRUE is currently allowed only for Ethernet connections. If set to FALSE, the activation can continue only after a successful authentication.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PAC_FILE N_("UTF-8 encoded file path containing PAC for EAP-FAST.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PASSWORD N_("UTF-8 encoded password used for EAP authentication methods. If both the \"password\" property and the \"password-raw\" property are specified, \"password\" is preferred.")
@@ -59,23 +61,23 @@
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES N_("List of strings to be matched against the altSubjectName of the certificate presented by the authentication server during the inner \"phase 2\" authentication. If the list is empty, no verification of the server certificate's altSubjectName is performed.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_AUTH N_("Specifies the allowed \"phase 2\" inner authentication method when an EAP method that uses an inner TLS tunnel is specified in the \"eap\" property.  For TTLS this property selects one of the supported non-EAP inner methods: \"pap\", \"chap\", \"mschap\", \"mschapv2\" while \"phase2-autheap\" selects an EAP inner method.  For PEAP this selects an inner EAP method, one of: \"gtc\", \"otp\", \"md5\" and \"tls\". Each \"phase 2\" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details. Both \"phase2-auth\" and \"phase2-autheap\" cannot be specified.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_AUTHEAP N_("Specifies the allowed \"phase 2\" inner EAP-based authentication method when TTLS is specified in the \"eap\" property.  Recognized EAP-based \"phase 2\" methods are \"md5\", \"mschapv2\", \"otp\", \"gtc\", and \"tls\". Each \"phase 2\" inner method requires specific parameters for successful authentication; see the wpa_supplicant documentation for more details.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT N_("Contains the \"phase 2\" CA certificate if used by the EAP method specified in the \"phase2-auth\" or \"phase2-autheap\" properties. Certificate data is specified using a \"scheme\"; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT N_("Contains the path to the \"phase 2\" CA certificate if used by the EAP method specified in the 802-1x.phase2-auth or 802-1x.phase2-autheap properties. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling 802-1x.system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD N_("The password used to access the \"phase2\" CA certificate stored in \"phase2-ca-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-ca-cert-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CA_PATH N_("UTF-8 encoded path to a directory containing PEM or DER formatted certificates to be added to the verification chain in addition to the certificate specified in the \"phase2-ca-cert\" property. If NMSetting8021x:system-ca-certs is enabled and the built-in CA path is an existing directory, then this setting is ignored.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT N_("Contains the \"phase 2\" client certificate if used by the EAP method specified in the \"phase2-auth\" or \"phase2-autheap\" properties. Certificate data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate's DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string \"file://\" and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT N_("Contains the path to the \"phase 2\" client certificate if used by the EAP method specified in the 802-1x.phase2-auth or 802-1x.phase2-autheap properties.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD N_("The password used to access the \"phase2\" client certificate stored in \"phase2-client-cert\" property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-client-cert-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH N_("Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner \"phase 2\" authentication. If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a \";\" delimited list.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH N_("Constraint for server domain name. If set, this FQDN is used as a suffix match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner \"phase 2\" authentication.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using same suffix match comparison. Since version 1.24, multiple valid FQDNs can be passed as a \";\" delimited list.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY N_("Contains the \"phase 2\" inner private key when the \"phase2-auth\" or \"phase2-autheap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"phase2-private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"phase2-private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the \"phase 2\" private key specified in the \"phase2-private-key\" property when the private key either uses the path scheme, or is a PKCS#12 format key.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY N_("The path to the \"phase 2\" inner private key when the 802-1x.phase2-auth or 802-1x.phase2-autheap property is set to \"tls\".")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the \"phase 2\" private key specified in the 802-1x.phase2-private-key property. This is normally used by secret agents, not directly by users.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"phase2-private-key-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server during the inner \"phase 2\" authentication. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PIN N_("PIN used for EAP authentication methods.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PIN_FLAGS N_("Flags indicating how to handle the \"pin\" property.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY N_("Contains the private key when the \"eap\" property is set to \"tls\". Key data is specified using a \"scheme\"; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key's encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the \"private-key-password\" property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string \"file://\" and ending with a terminating NUL byte, and as with the blob scheme the \"private-key-password\" property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: \"private-key\" is not a \"secret\" property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data.")
-#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the private key specified in the \"private-key\" property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key.")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY N_("The path to the private key when the 802-1.eap property is set to \"tls\".")
+#define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD N_("The password used to decrypt the private key specified in the 802-1x.private-key property. This is normally used by secret agents, not directly by users.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS N_("Flags indicating how to handle the \"private-key-password\" property.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_SUBJECT_MATCH N_("Substring to be matched against the subject of the certificate presented by the authentication server. When unset, no verification of the authentication server certificate's subject is performed. This property provides little security, if any, and should not be used.")
 #define DESCRIBE_DOC_NM_SETTING_802_1X_SYSTEM_CA_CERTS N_("When TRUE, overrides the \"ca-path\" and \"phase2-ca-path\" properties using the system CA directory specified at configure time with the --system-ca-path switch.  The certificates in this directory are added to the verification chain in addition to any certificates specified by the \"ca-cert\" and \"phase2-ca-cert\" properties. If the path provided with --system-ca-path is rather a file name (bundle of trusted CA certificates), it overrides \"ca-cert\" and \"phase2-ca-cert\" properties instead (sets ca_cert/ca_cert2 options for wpa_supplicant).")
@@ -173,6 +175,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_IAID N_("A string containing the \"Identity Association Identifier\" (IAID) used by the DHCP client. The string can be a 32-bit number (either decimal, hexadecimal or as colon separated hexadecimal numbers). Alternatively it can be set to the special values \"mac\", \"perm-mac\", \"ifname\" or \"stable\". When set to \"mac\" (or \"perm-mac\"), the last 4 bytes of the current (or permanent) MAC address are used as IAID. When set to \"ifname\", the IAID is computed by hashing the interface name. The special value \"stable\" can be used to generate an IAID based on the stable-id (see connection.stable-id), a per-host key and the interface name. When the property is unset, the value from global configuration is used; if no global default is set then the IAID is assumed to be \"ifname\". For DHCPv4, the IAID is only used with \"ipv4.dhcp-client-id\" values \"duid\" and \"ipv6-duid\" to generate the client-id. For DHCPv6, note that at the moment this property is only supported by the \"internal\" DHCPv6 plugin. The \"dhclient\" DHCPv6 plugin always derives the IAID from the MAC address. The actually used DHCPv6 IAID for a currently activated interface is exposed in the lease information of the device.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
+#define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_SEND_RELEASE N_("Whether the DHCP client will send RELEASE message when bringing the connection down. The default value is \"default\" (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as FALSE.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER N_("The Vendor Class Identifier DHCP option (60). Special characters in the data string may be escaped using C-style escapes, nevertheless this property cannot contain nul bytes. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the DHCP option is not sent to the server.")
 #define DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
@@ -204,6 +207,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_PD_HINT N_("A IPv6 address followed by a slash and a prefix length. If set, the value is sent to the DHCPv6 server as hint indicating the prefix delegation (IA_PD) we want to receive. To only hint a prefix length without prefix, set the address part to the zero address (for example \"::/60\").")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_REJECT_SERVERS N_("Array of servers from which DHCP offers must be rejected. This property is useful to avoid getting a lease from misconfigured or rogue servers. For DHCPv4, each element must be an IPv4 address, optionally followed by a slash and a prefix length (e.g. \"192.168.122.0/24\"). This property is currently not implemented for DHCPv6.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_HOSTNAME N_("If TRUE, a hostname is sent to the DHCP server when acquiring a lease. Some DHCP servers use this hostname to update DNS databases, essentially providing a static hostname for the computer.  If the \"dhcp-hostname\" property is NULL and this property is TRUE, the current persistent hostname of the computer is sent.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_SEND_RELEASE N_("Whether the DHCP client will send RELEASE message when bringing the connection down. The default value is \"default\" (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as FALSE.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DHCP_TIMEOUT N_("A timeout for a DHCP transaction in seconds. If zero (the default), a globally configured default is used. If still unspecified, a device specific timeout is used (usually 45 seconds). Set to 2147483647 (MAXINT32) for infinity.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS N_("Array of IP addresses of DNS servers. For DoT (DNS over TLS), the SNI server name can be specified by appending \"#example.com\" to the IP address of the DNS server. This currently only has effect when using systemd-resolved.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_OPTIONS N_("DNS options for /etc/resolv.conf as described in resolv.conf(5) manual. The currently supported options are \"attempts\", \"debug\", \"edns0\", \"ndots\", \"no-aaaa\", \"no-check-names\", \"no-reload\", \"no-tld-query\", \"rotate\", \"single-request\", \"single-request-reopen\", \"timeout\", \"trust-ad\", \"use-vc\" and \"inet6\", \"ip6-bytestring\", \"ip6-dotint\", \"no-ip6-dotint\". See the resolv.conf(5) manual. Note that there is a distinction between an unset (default) list and an empty list. In nmcli, to unset the list set the value to \"\". To set an empty list, set it to \" \". Currently, an unset list has the same meaning as an empty list. That might change in the future. The \"trust-ad\" setting is only honored if the profile contributes name servers to resolv.conf, and if all contributing profiles have \"trust-ad\" enabled. When using a caching DNS plugin (dnsmasq or systemd-resolved in NetworkManager.conf) then \"edns0\" and \"trust-ad\" are automatically added. The valid \"ipv4.dns-options\" and \"ipv6.dns-options\" get merged together.")
@@ -212,7 +216,7 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_GATEWAY N_("The gateway associated with this configuration. This is only meaningful if \"addresses\" is also set. Setting the gateway causes NetworkManager to configure a standard default route with the gateway as next hop. This is ignored if \"never-default\" is set. An alternative is to configure the default route explicitly with a manual route and /0 as prefix length. Note that the gateway usually conflicts with routing that NetworkManager configures for WireGuard interfaces, so usually it should not be set in that case. See \"ip4-auto-default-route\".")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured name servers and search domains are ignored and only name servers and search domains specified in the \"dns\" and \"dns-search\" properties, if any, are used.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES N_("When \"method\" is set to \"auto\" and this property to TRUE, automatically configured routes are ignored and only routes specified in the \"routes\" property, if any, are used.")
-#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IP6_PRIVACY N_("Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to \"-1\" (unknown) means fallback to global configuration \"ipv6.ip6-privacy\". If also global configuration is unspecified or set to \"-1\", fallback to read \"/proc/sys/net/ipv6/conf/default/use_tempaddr\". Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the \"addr-gen-mode\" property's \"stable-privacy\" setting as another way of avoiding host tracking with IPv6 addresses.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IP6_PRIVACY N_("Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). If set to \"-1\" (unknown) for a connection, the value is taken from the global \"ipv6.ip6-privacy\" setting. If the global setting is unspecified or also set to \"-1\", the value is set from the original value of \"/proc/sys/net/ipv6/conf/<iface>/use_tempaddr\" from before NetworkManager started. Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the \"addr-gen-mode\" property's \"stable-privacy\" setting as another way of avoiding host tracking with IPv6 addresses.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_MAY_FAIL N_("If TRUE, allow overall network configuration to proceed even if the configuration specified by this property times out.  Note that at least one IP configuration must succeed or overall network configuration will still fail.  For example, in IPv6-only networks, setting this property to TRUE on the NMSettingIP4Config allows the overall network configuration to succeed if IPv4 configuration fails but IPv6 configuration completes successfully.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_METHOD N_("The IPv6 connection method.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_MTU N_("Maximum transmission unit size, in bytes. If zero (the default), the MTU is set automatically from router advertisements or is left equal to the link-layer MTU. If greater than the link-layer MTU, or greater than zero but less than the minimum IPv6 MTU of 1280, this value has no effect.")
@@ -224,6 +228,8 @@
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTE_TABLE N_("Enable policy routing (source routing) and set the routing table used when adding routes. This affects all routes, including device-routes, IPv4LL, DHCP, SLAAC, default-routes and static routes. But note that static routes can individually overwrite the setting by explicitly specifying a non-zero routing table. If the table setting is left at zero, it is eligible to be overwritten via global configuration. If the property is zero even after applying the global configuration value, policy routing is disabled for the address family of this connection. Policy routing disabled means that NetworkManager will add all routes to the main table (except static routes that explicitly configure a different table). Additionally, NetworkManager will not delete any extraneous routes from tables except the main table. This is to preserve backward compatibility for users who manage routing tables outside of NetworkManager.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTES N_("Array of IP routes.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_ROUTING_RULES N_("A comma separated list of routing rules for policy routing.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_TEMP_PREFERRED_LIFETIME N_("The preferred lifetime of autogenerated temporary addresses, in seconds. If set to \"0\" (unknown) for a connection, the value is taken from the global \"ipv6.temp-preferred-lifetime\" setting. If the global setting is unspecified or also set to \"0\", the value is set from the original value of \"/proc/sys/net/ipv6/conf/<iface>/temp_prefered_lft\" from before NetworkManager started.")
+#define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_TEMP_VALID_LIFETIME N_("The valid lifetime of autogenerated temporary addresses, in seconds. If set to \"0\" (unknown) for a connection, the value is taken from the global \"ipv6.temp-valid-lifetime\" setting. If the global setting is unspecified or also set to \"0\", the value is set from the original value of \"/proc/sys/net/ipv6/conf/<iface>/temp_valid_lft\" from before NetworkManager started.")
 #define DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_TOKEN N_("Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02 IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode. When set, the token is used as IPv6 interface identifier instead of the hardware address. This only applies to addresses from stateless autoconfiguration, not to IPv6 link local addresses.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT N_("How many additional levels of encapsulation are permitted to be prepended to packets. This property applies only to IPv6 tunnels. To disable this option, add 0x1 (ip6-ign-encap-limit) to ip-tunnel flags.")
 #define DESCRIBE_DOC_NM_SETTING_IP_TUNNEL_FLAGS N_("Tunnel flags. Currently, the following values are supported: 0x1 (ip6-ign-encap-limit), 0x2 (ip6-use-orig-tclass), 0x4 (ip6-use-orig-flowlabel), 0x8 (ip6-mip6-dev), 0x10 (ip6-rcv-dscp-copy) and 0x20 (ip6-use-orig-fwmark). They are valid only for IPv6 tunnels.")
@@ -390,6 +396,7 @@
 #define DESCRIBE_DOC_NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK N_("With \"cloned-mac-address\" setting \"random\" or \"stable\", by default all bits of the MAC address are scrambled and a locally-administered, unicast MAC address is created. This property allows to specify that certain bits are fixed. Note that the least significant bit of the first MAC address will always be unset to create a unicast MAC address. If the property is NULL, it is eligible to be overwritten by a default connection setting. If the value is still NULL or an empty string, the default is to create a locally-administered, unicast MAC address. If the value contains one MAC address, this address is used as mask. The set bits of the mask are to be filled with the current MAC address of the device, while the unset bits are subject to randomization. Setting \"FE:FF:FF:00:00:00\" means to preserve the OUI of the current MAC address and only randomize the lower 3 bytes using the \"random\" or \"stable\" algorithm. If the value contains one additional MAC address after the mask, this address is used instead of the current MAC address to fill the bits that shall not be randomized. For example, a value of \"FE:FF:FF:00:00:00 68:F7:28:00:00:00\" will set the OUI of the MAC address to 68:F7:28, while the lower bits are randomized. A value of \"02:00:00:00:00:00 00:00:00:00:00:00\" will create a fully scrambled globally-administered, burned-in MAC address. If the value contains more than one additional MAC addresses, one of them is chosen randomly. For example, \"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00\" will create a fully scrambled MAC address, randomly locally or globally administered.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_MAC_ADDRESS N_("If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST N_("If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55).")
+#define DESCRIBE_DOC_NM_SETTING_WIRED_MAC_ADDRESS_DENYLIST N_("If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55).")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_PORT N_("Specific port type to use if the device supports multiple attachment methods.  One of \"tp\" (Twisted Pair), \"aui\" (Attachment Unit Interface), \"bnc\" (Thin Ethernet) or \"mii\" (Media Independent Interface). If the device supports only one port type, this setting is ignored.")
 #define DESCRIBE_DOC_NM_SETTING_WIRED_S390_NETTYPE N_("s390 network device type; one of \"qeth\", \"lcs\", or \"ctc\", representing the different types of virtual network devices available on s390 systems.")
@@ -415,6 +422,7 @@
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_HIDDEN N_("If TRUE, indicates that the network is a non-broadcasting network that hides its SSID. This works both in infrastructure and AP mode. In infrastructure mode, various workarounds are used for a more reliable discovery of hidden networks, such as probe-scanning the SSID.  However, these workarounds expose inherent insecurities with hidden SSID networks, and thus hidden SSID networks should be used with caution. In AP mode, the created network does not broadcast its SSID. Note that marking the network as hidden may be a privacy issue for you (in infrastructure mode) or client stations (in AP mode), as the explicit probe-scans are distinctly recognizable on the air.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS N_("If specified, this connection will only apply to the Wi-Fi device whose permanent MAC address matches. This property does not change the MAC address of the device (i.e. MAC spoofing).")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST N_("A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\").")
+#define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_DENYLIST N_("A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg \"00:11:22:33:44:55\").")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION N_("One of \"default\" (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  \"never\" (1) (never randomize the MAC address), or \"always\" (2) (always randomize the MAC address).")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MODE N_("Wi-Fi network mode; one of \"infrastructure\", \"mesh\", \"adhoc\" or \"ap\".  If blank, infrastructure is assumed.")
 #define DESCRIBE_DOC_NM_SETTING_WIRELESS_MTU N_("If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.")
diff --git a/src/libnmt-newt/README.md b/src/libnmt-newt/README.md
new file mode 100644
index 00000000..386a86cb
--- /dev/null
+++ b/src/libnmt-newt/README.md
@@ -0,0 +1,4 @@
+libnmt-newt
+===========
+
+A helper library of UI elements on top of libnewt and libnm, for nmtui.
diff --git a/src/linux-headers/README.md b/src/linux-headers/README.md
new file mode 100644
index 00000000..06cdd301
--- /dev/null
+++ b/src/linux-headers/README.md
@@ -0,0 +1,15 @@
+linux-headers
+=============
+
+Contains a copy of Linux UAPI kernel headers.
+When we build against an older kernel, we may
+still want to unconditionally build against a
+certain version of kernel API.
+
+These headers should be taken without modification
+from Linux.
+
+Don't include any of these these headers directly, instead
+include "libnm-std-aux/nm-linux-compat.h" which drags these
+headers in. This ensures that we include at all places our own
+patched variant, instead of the system headers.
diff --git a/src/meson.build b/src/meson.build
index 92e95e68..ceeee6a0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -69,6 +69,14 @@ libn_dhcp4 = static_library(
 
 ###############################################################################
 
+libnm_systemd_common_cflags = [ ]
+
+libnm_systemd_common_cflags += cc.get_supported_arguments([
+  '-Wno-nonnull-compare',
+])
+
+###############################################################################
+
 subdir('libnm-std-aux')
 subdir('libnm-glib-aux')
 subdir('libnm-log-null')
diff --git a/src/n-acd/.editorconfig b/src/n-acd/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/n-acd/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 8
diff --git a/src/n-acd/.github/workflows/ci.yml b/src/n-acd/.github/workflows/ci.yml
new file mode 100644
index 00000000..22fc8141
--- /dev/null
+++ b/src/n-acd/.github/workflows/ci.yml
@@ -0,0 +1,122 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  ci:
+    name: CI with Default Configuration
+    runs-on: ubuntu-latest
+
+    steps:
+    #
+    # Prepare CI
+    #
+    # We cannot use the github-action of the `ci-c-util` project, because we
+    # need privileges in the container. Therefore, fetch the CI sources and
+    # build the container manually.
+    #
+    - name: Fetch CI
+      uses: actions/checkout@v2
+      with:
+        repository: c-util/automation
+        ref: v1
+        path: automation
+    - name: Build CI
+      working-directory: automation/src/ci-c-util
+      run: docker build --tag ci-c-util:v1 .
+
+    #
+    # Run CI
+    #
+    # Take the CI image we built and run the CI with the default project
+    # configuration. We do not use valgrind, since it falls-over with bpf(2)
+    # syscalls.
+    #
+    - name: Fetch Sources
+      uses: actions/checkout@v2
+      with:
+        path: source
+    - name: Run through C-Util CI
+      run: |
+        docker run \
+                --privileged \
+                -v "$(pwd)/source:/github/workspace" \
+                "ci-c-util:v1" \
+                "--m32=1" \
+                "--source=/github/workspace"
+
+  ci-no-ebpf:
+    name: CI without eBPF
+    runs-on: ubuntu-latest
+
+    steps:
+    # See above in 'ci' job.
+    - name: Fetch CI
+      uses: actions/checkout@v2
+      with:
+        repository: c-util/automation
+        ref: v1
+        path: automation
+    - name: Build CI
+      working-directory: automation/src/ci-c-util
+      run: docker build --tag ci-c-util:v1 .
+
+    #
+    # Run CI
+    #
+    # This again runs the CI, but this time disables eBPF. We do support the
+    # legacy BPF fallback, so lets make sure we test for it.
+    #
+    - name: Fetch Sources
+      uses: actions/checkout@v2
+      with:
+        path: source
+    - name: Run through C-Util CI
+      run: |
+        docker run \
+                --privileged \
+                -v "$(pwd)/source:/github/workspace" \
+                "ci-c-util:v1" \
+                "--m32=1" \
+                "--mesonargs=-Debpf=false" \
+                "--source=/github/workspace"
+
+  ci-valgrind:
+    name: CI through Valgrind
+    runs-on: ubuntu-latest
+
+    steps:
+    # See above in 'ci' job.
+    - name: Fetch CI
+      uses: actions/checkout@v2
+      with:
+        repository: c-util/automation
+        ref: v1
+        path: automation
+    - name: Build CI
+      working-directory: automation/src/ci-c-util
+      run: docker build --tag ci-c-util:v1 .
+
+    #
+    # Run CI
+    #
+    # This again runs the CI, but this time through valgrind. Since some
+    # syscalls are not implemented on x86-64 32bit compat (e.g., bpf(2)), we
+    # disable the m32 mode.
+    #
+    - name: Fetch Sources
+      uses: actions/checkout@v2
+      with:
+        path: source
+    - name: Run through C-Util CI
+      run: |
+        docker run \
+                --privileged \
+                -v "$(pwd)/source:/github/workspace" \
+                "ci-c-util:v1" \
+                "--source=/github/workspace" \
+                "--valgrind=1"
diff --git a/src/n-acd/.gitmodules b/src/n-acd/.gitmodules
new file mode 100644
index 00000000..04829bdb
--- /dev/null
+++ b/src/n-acd/.gitmodules
@@ -0,0 +1,12 @@
+[submodule "subprojects/c-list"]
+	path = subprojects/c-list
+	url = https://github.com/c-util/c-list.git
+[submodule "subprojects/c-siphash"]
+	path = subprojects/c-siphash
+	url = https://github.com/c-util/c-siphash.git
+[submodule "subprojects/c-rbtree"]
+	path = subprojects/c-rbtree
+	url = https://github.com/c-util/c-rbtree.git
+[submodule "subprojects/c-stdaux"]
+	path = subprojects/c-stdaux
+	url = https://github.com/c-util/c-stdaux.git
diff --git a/src/n-acd/AUTHORS b/src/n-acd/AUTHORS
new file mode 100644
index 00000000..98ff1482
--- /dev/null
+++ b/src/n-acd/AUTHORS
@@ -0,0 +1,39 @@
+LICENSE:
+        This project is dual-licensed under both the Apache License, Version
+        2.0, and the GNU Lesser General Public License, Version 2.1+.
+
+AUTHORS-ASL:
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+                http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+
+AUTHORS-LGPL:
+        This program is free software; you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published
+        by the Free Software Foundation; either version 2.1 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public License
+        along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+COPYRIGHT: (ordered alphabetically)
+        Copyright (C) 2015-2019 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        Beniamino Galvani <bgalvani@redhat.com>
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Thomas Haller <thaller@redhat.com>
+        Tom Gundersen <teg@jklm.no>
diff --git a/src/n-acd/NEWS.md b/src/n-acd/NEWS.md
new file mode 100644
index 00000000..7a9ccd67
--- /dev/null
+++ b/src/n-acd/NEWS.md
@@ -0,0 +1,46 @@
+# n-acd - IPv4 Address Conflict Detection
+
+## CHANGES WITH 2:
+
+        * All public destructors now include a variant that returns `void`.
+          This was requested for easier integration with `glib` and friends.
+          Similar to the `cleanup` variants, these variants are denoted by a
+          single-character function-name suffix. E.g., `n_acd_freev()`
+
+        * A fallback to `CLOCK_MONOTONIC` is now provided in case
+          `CLOCK_BOOTTIME` is not supported by the kernel. Note that this is in
+          no way signalled through the API, so if timers should follow the
+          `BOOTTIME` rather than monotonic clock, a kernel with this clock is
+          required.
+
+        * The `c-sundry` dependency is no longer needed.
+
+        * The `transport` configuration property is now mandatory for
+          `n_acd_new()`. It defaulted to `ETHERNET` before, by mistake.
+
+        * In-source documentation for the public API is now provided.
+
+        Contributions from: Beniamino Galvani, David Herrmann, David
+                            Rheinsberg, Thomas Haller, Tom Gundersen
+
+        - Tübingen, 2019-03-20
+
+## CHANGES WITH 1:
+
+        * Initial release of n-acd. This project implements the IPv4 Address
+          Conflict Detection standard as defined in RFC-5227. The state machine
+          is implemented in a shared library and provides a stable ISO-C11 API.
+          The implementation is linux-only and relies heavily on the API
+          behavior of recent linux kernel releases.
+
+        * Compared to the pre-releases, this release supports many parallel
+          probes on a single n-acd context. This reduces the number of
+          allocated network resources to O(1), based on the number of running
+          parallel probes.
+
+        * The n-acd project is now dual-licensed: ASL-2.0 and LGPL-2.1+
+
+        Contributions from: Beniamino Galvani, David Herrmann, Thomas Haller,
+                            Tom Gundersen
+
+        - Tübingen, 2018-08-08
diff --git a/src/n-acd/README.md b/src/n-acd/README.md
new file mode 100644
index 00000000..08954182
--- /dev/null
+++ b/src/n-acd/README.md
@@ -0,0 +1,60 @@
+n-acd
+=====
+
+IPv4 Address Conflict Detection
+
+The n-acd project implements the IPv4 Address Conflict Detection standard as
+defined in RFC-5227. The state machine is implemented in a shared library and
+provides a stable ISO-C11 API. The implementation is linux-only and relies
+heavily on the API behavior of recent linux kernel releases.
+
+### Project
+
+ * **Website**: <https://nettools.github.io/n-acd>
+ * **Bug Tracker**: <https://github.com/nettools/n-acd/issues>
+ * **Mailing-List**: <https://groups.google.com/forum/#!forum/nettools-devel>
+
+### Requirements
+
+The requirements for this project are:
+
+ * `Linux kernel >= 3.19`
+ * `libc` (e.g., `glibc >= 2.16`)
+
+At build-time, the following software is required:
+
+ * `meson >= 0.41`
+ * `pkg-config >= 0.29`
+
+### Build
+
+The meson build-system is used for this project. Contact upstream
+documentation for detailed help. In most situations the following
+commands are sufficient to build and install from source:
+
+```sh
+mkdir build
+cd build
+meson setup ..
+ninja
+meson test
+ninja install
+```
+
+The following configuration options are available:
+
+ * `ebpf`: This boolean controls whether `ebpf` features are used to improve
+           the package filtering performance. If disabled, classic bpf will be
+           used. This feature requires a rather recent kernel (>=3.19).
+           Default is: true
+
+### Repository:
+
+ - **web**:   <https://github.com/nettools/n-acd>
+ - **https**: `https://github.com/nettools/n-acd.git`
+ - **ssh**:   `git@github.com:nettools/n-acd.git`
+
+### License:
+
+ - **Apache-2.0** OR **LGPL-2.1-or-later**
+ - See AUTHORS file for details.
diff --git a/src/n-acd/meson.build b/src/n-acd/meson.build
new file mode 100644
index 00000000..6479eb1a
--- /dev/null
+++ b/src/n-acd/meson.build
@@ -0,0 +1,27 @@
+project(
+        'n-acd',
+        'c',
+        version: '2',
+        license: 'Apache',
+        default_options: [
+                'c_std=c11',
+        ],
+)
+project_description = 'IPv4 Address Conflict Detection'
+
+add_project_arguments('-D_GNU_SOURCE', language: 'c')
+mod_pkgconfig = import('pkgconfig')
+
+sub_clist = subproject('c-list')
+sub_crbtree = subproject('c-rbtree')
+sub_csiphash = subproject('c-siphash')
+sub_cstdaux = subproject('libcstdaux-1')
+
+dep_clist = sub_clist.get_variable('libclist_dep')
+dep_crbtree = sub_crbtree.get_variable('libcrbtree_dep')
+dep_csiphash = sub_csiphash.get_variable('libcsiphash_dep')
+dep_cstdaux = sub_cstdaux.get_variable('libcstdaux_dep')
+
+use_ebpf = get_option('ebpf')
+
+subdir('src')
diff --git a/src/n-acd/meson_options.txt b/src/n-acd/meson_options.txt
new file mode 100644
index 00000000..b024ee1d
--- /dev/null
+++ b/src/n-acd/meson_options.txt
@@ -0,0 +1 @@
+option('ebpf', type: 'boolean', value: true, description: 'Enable eBPF packet filtering')
diff --git a/src/n-acd/src/libnacd.sym b/src/n-acd/src/libnacd.sym
new file mode 100644
index 00000000..f85e13ac
--- /dev/null
+++ b/src/n-acd/src/libnacd.sym
@@ -0,0 +1,28 @@
+LIBNACD_2 {
+global:
+        n_acd_config_new;
+        n_acd_config_free;
+        n_acd_config_set_ifindex;
+        n_acd_config_set_transport;
+        n_acd_config_set_mac;
+
+        n_acd_probe_config_new;
+        n_acd_probe_config_free;
+        n_acd_probe_config_set_ip;
+        n_acd_probe_config_set_timeout;
+
+        n_acd_new;
+        n_acd_ref;
+        n_acd_unref;
+        n_acd_get_fd;
+        n_acd_dispatch;
+        n_acd_pop_event;
+        n_acd_probe;
+
+        n_acd_probe_free;
+        n_acd_probe_set_userdata;
+        n_acd_probe_get_userdata;
+        n_acd_probe_announce;
+local:
+       *;
+};
diff --git a/src/n-acd/src/meson.build b/src/n-acd/src/meson.build
new file mode 100644
index 00000000..3e92681f
--- /dev/null
+++ b/src/n-acd/src/meson.build
@@ -0,0 +1,95 @@
+#
+# target: libnacd.so
+#
+
+libnacd_symfile = join_paths(meson.current_source_dir(), 'libnacd.sym')
+
+libnacd_deps = [
+        dep_clist,
+        dep_crbtree,
+        dep_csiphash,
+        dep_cstdaux,
+]
+
+libnacd_sources = [
+        'n-acd.c',
+        'n-acd-probe.c',
+        'util/timer.c',
+]
+
+if use_ebpf
+        libnacd_sources += [
+                'n-acd-bpf.c',
+        ]
+else
+        libnacd_sources += [
+                'n-acd-bpf-fallback.c',
+        ]
+endif
+
+libnacd_private = static_library(
+        'nacd-private',
+        libnacd_sources,
+        c_args: [
+                '-fvisibility=hidden',
+                '-fno-common'
+        ],
+        dependencies: libnacd_deps,
+        pic: true,
+)
+
+libnacd_shared = shared_library(
+        'nacd',
+        objects: libnacd_private.extract_all_objects(),
+        dependencies: libnacd_deps,
+        install: not meson.is_subproject(),
+        soversion: 0,
+        link_depends: libnacd_symfile,
+        link_args: [
+                '-Wl,--no-undefined',
+                '-Wl,--version-script=@0@'.format(libnacd_symfile)
+        ],
+)
+
+libnacd_dep = declare_dependency(
+        include_directories: include_directories('.'),
+        link_with: libnacd_private,
+        dependencies: libnacd_deps,
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers('n-acd.h')
+
+        mod_pkgconfig.generate(
+                libraries: libnacd_shared,
+                version: meson.project_version(),
+                name: 'libnacd',
+                filebase: 'libnacd',
+                description: project_description,
+        )
+endif
+
+#
+# target: test-*
+#
+
+test_api = executable('test-api', ['test-api.c'], link_with: libnacd_shared)
+test('API Symbol Visibility', test_api)
+
+if use_ebpf
+        test_bpf = executable('test-bpf', ['test-bpf.c'], dependencies: libnacd_dep)
+        test('eBPF socket filtering', test_bpf)
+endif
+
+test_loopback = executable('test-loopback', ['test-loopback.c'], dependencies: libnacd_dep)
+test('Echo Suppression via Loopback', test_loopback)
+
+test_timer = executable('test-timer', ['util/test-timer.c'], dependencies: libnacd_dep)
+test('Timer helper', test_timer)
+
+#test_unplug = executable('test-unplug', ['test-unplug.c'], dependencies: libnacd_dep)
+#test('Async Interface Hotplug', test_unplug)
+
+test_veth = executable('test-veth', ['test-veth.c'], dependencies: libnacd_dep)
+test('Parallel ACD instances', test_veth)
diff --git a/src/n-acd/src/test-api.c b/src/n-acd/src/test-api.c
new file mode 100644
index 00000000..70f75208
--- /dev/null
+++ b/src/n-acd/src/test-api.c
@@ -0,0 +1,88 @@
+/*
+ * Tests for n-acd API
+ * This verifies the visibility and availability of the public API.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdlib.h>
+#include "n-acd.h"
+
+static void test_api_constants(void) {
+        assert(1 + N_ACD_TIMEOUT_RFC5227);
+
+        assert(1 + _N_ACD_E_SUCCESS);
+        assert(1 + N_ACD_E_PREEMPTED);
+        assert(1 + N_ACD_E_INVALID_ARGUMENT);
+        assert(1 + _N_ACD_E_N);
+
+        assert(1 + N_ACD_TRANSPORT_ETHERNET);
+        assert(1 + _N_ACD_TRANSPORT_N);
+
+        assert(1 + N_ACD_EVENT_READY);
+        assert(1 + N_ACD_EVENT_USED);
+        assert(1 + N_ACD_EVENT_DEFENDED);
+        assert(1 + N_ACD_EVENT_CONFLICT);
+        assert(1 + N_ACD_EVENT_DOWN);
+        assert(1 + _N_ACD_EVENT_N);
+
+        assert(1 + N_ACD_DEFEND_NEVER);
+        assert(1 + N_ACD_DEFEND_ONCE);
+        assert(1 + N_ACD_DEFEND_ALWAYS);
+        assert(1 + _N_ACD_DEFEND_N);
+}
+
+static void test_api_types(void) {
+        assert(sizeof(NAcdEvent*));
+        assert(sizeof(NAcdConfig*));
+        assert(sizeof(NAcdProbeConfig*));
+        assert(sizeof(NAcd*));
+        assert(sizeof(NAcdProbe*));
+}
+
+static void test_api_functions(void) {
+        void *fns[] = {
+                (void *)n_acd_config_new,
+                (void *)n_acd_config_free,
+                (void *)n_acd_config_set_ifindex,
+                (void *)n_acd_config_set_transport,
+                (void *)n_acd_config_set_mac,
+                (void *)n_acd_probe_config_new,
+                (void *)n_acd_probe_config_free,
+                (void *)n_acd_probe_config_set_ip,
+                (void *)n_acd_probe_config_set_timeout,
+
+                (void *)n_acd_new,
+                (void *)n_acd_ref,
+                (void *)n_acd_unref,
+                (void *)n_acd_get_fd,
+                (void *)n_acd_dispatch,
+                (void *)n_acd_pop_event,
+                (void *)n_acd_probe,
+
+                (void *)n_acd_probe_free,
+                (void *)n_acd_probe_set_userdata,
+                (void *)n_acd_probe_get_userdata,
+                (void *)n_acd_probe_announce,
+
+                (void *)n_acd_config_freep,
+                (void *)n_acd_config_freev,
+                (void *)n_acd_probe_config_freep,
+                (void *)n_acd_probe_config_freev,
+                (void *)n_acd_unrefp,
+                (void *)n_acd_unrefv,
+                (void *)n_acd_probe_freep,
+                (void *)n_acd_probe_freev,
+        };
+        size_t i;
+
+        for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
+                assert(!!fns[i]);
+}
+
+int main(int argc, char **argv) {
+        test_api_constants();
+        test_api_types();
+        test_api_functions();
+        return 0;
+}
diff --git a/src/n-acd/src/test-bpf.c b/src/n-acd/src/test-bpf.c
new file mode 100644
index 00000000..78f9d0f1
--- /dev/null
+++ b/src/n-acd/src/test-bpf.c
@@ -0,0 +1,226 @@
+/*
+ * eBPF socket filter tests
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "n-acd.h"
+#include "n-acd-private.h"
+#include "test.h"
+
+#define ETHER_ARP_PACKET_INIT(_op, _mac, _sip, _tip) {                  \
+                .ea_hdr = {                                             \
+                        .ar_hrd = htobe16(ARPHRD_ETHER),                \
+                        .ar_pro = htobe16(ETHERTYPE_IP),                \
+                        .ar_hln = 6,                                    \
+                        .ar_pln = 4,                                    \
+                        .ar_op = htobe16(_op),                          \
+                },                                                      \
+                .arp_sha[0] = (_mac)->ether_addr_octet[0],              \
+                .arp_sha[1] = (_mac)->ether_addr_octet[1],              \
+                .arp_sha[2] = (_mac)->ether_addr_octet[2],              \
+                .arp_sha[3] = (_mac)->ether_addr_octet[3],              \
+                .arp_sha[4] = (_mac)->ether_addr_octet[4],              \
+                .arp_sha[5] = (_mac)->ether_addr_octet[5],              \
+                .arp_spa[0] = (be32toh((_sip)->s_addr) >> 24) & 0xff,   \
+                .arp_spa[1] = (be32toh((_sip)->s_addr) >> 16) & 0xff,   \
+                .arp_spa[2] = (be32toh((_sip)->s_addr) >> 8) & 0xff,    \
+                .arp_spa[3] =  be32toh((_sip)->s_addr) & 0xff,          \
+                .arp_tpa[0] = (be32toh((_tip)->s_addr) >> 24) & 0xff,   \
+                .arp_tpa[1] = (be32toh((_tip)->s_addr) >> 16) & 0xff,   \
+                .arp_tpa[2] = (be32toh((_tip)->s_addr) >> 8) & 0xff,    \
+                .arp_tpa[3] =  be32toh((_tip)->s_addr) & 0xff,          \
+        }
+
+static void test_map(void) {
+        int r, mapfd = -1;
+        struct in_addr addr = { 1 };
+
+        r = n_acd_bpf_map_create(&mapfd, 8);
+        c_assert(r >= 0);
+        c_assert(mapfd >= 0);
+
+        r = n_acd_bpf_map_remove(mapfd, &addr);
+        c_assert(r == -ENOENT);
+
+        r = n_acd_bpf_map_add(mapfd, &addr);
+        c_assert(r >= 0);
+
+        r = n_acd_bpf_map_add(mapfd, &addr);
+        c_assert(r == -EEXIST);
+
+        r = n_acd_bpf_map_remove(mapfd, &addr);
+        c_assert(r >= 0);
+
+        r = n_acd_bpf_map_remove(mapfd, &addr);
+        c_assert(r == -ENOENT);
+
+        close(mapfd);
+}
+
+static void verify_success(struct ether_arp *packet, int out_fd, int in_fd) {
+        uint8_t buf[sizeof(struct ether_arp)];
+        int r;
+
+        r = send(out_fd, packet, sizeof(struct ether_arp), 0);
+        c_assert(r == sizeof(struct ether_arp));
+
+        r = recv(in_fd, buf, sizeof(buf), 0);
+        c_assert(r == sizeof(struct ether_arp));
+}
+
+static void verify_failure(struct ether_arp *packet, int out_fd, int in_fd) {
+        uint8_t buf[sizeof(struct ether_arp)];
+        int r;
+
+        r = send(out_fd, packet, sizeof(struct ether_arp), 0);
+        c_assert(r == sizeof(struct ether_arp));
+
+        r = recv(in_fd, buf, sizeof(buf), 0);
+        c_assert(r < 0);
+        c_assert(errno == EAGAIN);
+}
+
+static void test_filter(void) {
+        uint8_t buf[sizeof(struct ether_arp) + 1] = {};
+        struct ether_addr mac1 = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 } };
+        struct ether_addr mac2 = { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x07 } };
+        struct in_addr ip0 = { 0 };
+        struct in_addr ip1 = { 1 };
+        struct in_addr ip2 = { 2 };
+        struct ether_arp *packet = (struct ether_arp *)buf;
+        int r, mapfd = -1, progfd = -1, pair[2];
+
+        r = n_acd_bpf_map_create(&mapfd, 1);
+        c_assert(r >= 0);
+
+        r = n_acd_bpf_compile(&progfd, mapfd, &mac1);
+        c_assert(r >= 0);
+        c_assert(progfd >= 0);
+
+        r = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, pair);
+        c_assert(r >= 0);
+
+        r = setsockopt(pair[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd,
+                       sizeof(progfd));
+        c_assert(r >= 0);
+
+        r = n_acd_bpf_map_add(mapfd, &ip1);
+        c_assert(r >= 0);
+
+        /* valid */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        verify_success(packet, pair[0], pair[1]);
+
+        /* valid: reply instead of request */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REPLY, &mac2, &ip1, &ip2);
+        verify_success(packet, pair[0], pair[1]);
+
+        /* valid: to us instead of from us */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip0, &ip1);
+        verify_success(packet, pair[0], pair[1]);
+
+        /* invalid header type */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        packet->arp_hrd += 1;
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* invalid protocol */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        packet->arp_pro += 1;
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* invalid hw addr length */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        packet->arp_hln += 1;
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* invalid protocol addr length */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        packet->arp_pln += 1;
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* invalid operation */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_NAK, &mac2, &ip1, &ip2);
+        packet->arp_hln += 1;
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* own mac */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac1, &ip1, &ip2);
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* not to, nor from us, with source */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip2, &ip2);
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* not to, nor from us, without source */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip0, &ip2);
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* to us instead of from us, but reply */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REPLY, &mac2, &ip0, &ip1);
+        verify_failure(packet, pair[0], pair[1]);
+
+        /* long */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        r = send(pair[0], buf, sizeof(struct ether_arp) + 1, 0);
+        c_assert(r == sizeof(struct ether_arp) + 1);
+
+        r = recv(pair[1], buf, sizeof(buf), 0);
+        c_assert(r == sizeof(struct ether_arp));
+
+        /* short */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        r = send(pair[0], buf, sizeof(struct ether_arp) - 1, 0);
+        c_assert(r == sizeof(struct ether_arp) - 1);
+
+        r = recv(pair[1], buf, sizeof(buf), 0);
+        c_assert(r < 0);
+        c_assert(errno == EAGAIN);
+
+        /*
+         * Send one packet before and one packet after modifying the map,
+         * verify that the modification applies at the time of send(), not recv().
+         */
+        *packet = (struct ether_arp)ETHER_ARP_PACKET_INIT(ARPOP_REQUEST, &mac2, &ip1, &ip2);
+        r = send(pair[0], buf, sizeof(struct ether_arp), 0);
+        c_assert(r == sizeof(struct ether_arp));
+
+        r = n_acd_bpf_map_remove(mapfd, &ip1);
+        c_assert(r >= 0);
+
+        r = send(pair[0], buf, sizeof(struct ether_arp), 0);
+        c_assert(r == sizeof(struct ether_arp));
+
+        r = recv(pair[1], buf, sizeof(buf), 0);
+        c_assert(r == sizeof(struct ether_arp));
+
+        r = recv(pair[1], buf, sizeof(buf), 0);
+        c_assert(r < 0);
+        c_assert(errno == EAGAIN);
+
+        close(pair[0]);
+        close(pair[1]);
+        close(progfd);
+        close(mapfd);
+}
+
+int main(int argc, char **argv) {
+        test_setup();
+
+        test_map();
+        test_filter();
+
+        return 0;
+}
diff --git a/src/n-acd/src/test-loopback.c b/src/n-acd/src/test-loopback.c
new file mode 100644
index 00000000..0671cf66
--- /dev/null
+++ b/src/n-acd/src/test-loopback.c
@@ -0,0 +1,82 @@
+/*
+ * Test on loopback device
+ * This runs the ACD engine on the loopback device, effectively testing the BPF
+ * filter of ACD to discard its own packets. This might happen on
+ * non-spanning-tree networks, or on networks that echo packets.
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include "test.h"
+
+static void test_loopback(int ifindex, uint8_t *mac, size_t n_mac) {
+        NAcdConfig *config;
+        NAcd *acd;
+        struct pollfd pfds;
+        int r, fd;
+
+        r = n_acd_config_new(&config);
+        c_assert(!r);
+
+        n_acd_config_set_ifindex(config, ifindex);
+        n_acd_config_set_transport(config, N_ACD_TRANSPORT_ETHERNET);
+        n_acd_config_set_mac(config, mac, n_mac);
+
+        r = n_acd_new(&acd, config);
+        c_assert(!r);
+
+        n_acd_config_free(config);
+
+        {
+                NAcdProbeConfig *probe_config;
+                NAcdProbe *probe;
+                struct in_addr ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) };
+
+                r = n_acd_probe_config_new(&probe_config);
+                c_assert(!r);
+
+                n_acd_probe_config_set_ip(probe_config, ip);
+                n_acd_probe_config_set_timeout(probe_config, 100);
+
+                r = n_acd_probe(acd, &probe, probe_config);
+                c_assert(!r);
+
+                n_acd_probe_config_free(probe_config);
+
+                n_acd_get_fd(acd, &fd);
+
+                for (;;) {
+                        NAcdEvent *event;
+                        pfds = (struct pollfd){ .fd = fd, .events = POLLIN };
+                        r = poll(&pfds, 1, -1);
+                        c_assert(r >= 0);
+
+                        r = n_acd_dispatch(acd);
+                        c_assert(!r);
+
+                        r = n_acd_pop_event(acd, &event);
+                        c_assert(!r);
+                        if (event) {
+                                c_assert(event->event == N_ACD_EVENT_READY);
+                                break;
+                        }
+                }
+
+                n_acd_probe_free(probe);
+        }
+
+        n_acd_unref(acd);
+}
+
+int main(int argc, char **argv) {
+        struct ether_addr mac;
+        int ifindex;
+
+        test_setup();
+
+        test_loopback_up(&ifindex, &mac);
+        test_loopback(ifindex, mac.ether_addr_octet, sizeof(mac.ether_addr_octet));
+
+        return 0;
+}
diff --git a/src/n-acd/src/test-twice.c b/src/n-acd/src/test-twice.c
new file mode 100644
index 00000000..b474502e
--- /dev/null
+++ b/src/n-acd/src/test-twice.c
@@ -0,0 +1,97 @@
+/*
+ * Test with unused address twice in parallel
+ * This runs the ACD engine with an unused address on a veth pair, but it runs
+ * it on both ends. We expect the PROBE to fail on at least one of the devices.
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include "test.h"
+
+static void test_unused(int ifindex1, uint8_t *mac1, size_t n_mac1, int ifindex2, uint8_t *mac2, size_t n_mac2) {
+        NAcdConfig config1 = {
+                .ifindex = ifindex1,
+                .transport = N_ACD_TRANSPORT_ETHERNET,
+                .mac = mac1,
+                .n_mac = n_mac1,
+                .ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
+                .timeout_msec = 100,
+        };
+        NAcdConfig config2 = {
+                .ifindex = ifindex2,
+                .transport = N_ACD_TRANSPORT_ETHERNET,
+                .mac = mac2,
+                .n_mac = n_mac2,
+                .ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
+                .timeout_msec = 100,
+        };
+        struct pollfd pfds[2];
+        NAcd *acd1, *acd2;
+        int r, fd1, fd2, state1, state2;
+
+        r = n_acd_new(&acd1);
+        c_assert(!r);
+        r = n_acd_new(&acd2);
+        c_assert(!r);
+
+        n_acd_get_fd(acd1, &fd1);
+        n_acd_get_fd(acd2, &fd2);
+
+        r = n_acd_start(acd1, &config1);
+        c_assert(!r);
+        r = n_acd_start(acd2, &config2);
+        c_assert(!r);
+
+        for (state1 = state2 = -1; state1 == -1 || state2 == -1; ) {
+                NAcdEvent *event;
+                pfds[0] = (struct pollfd){ .fd = fd1, .events = (state1 == -1) ? POLLIN : 0 };
+                pfds[1] = (struct pollfd){ .fd = fd2, .events = (state2 == -1) ? POLLIN : 0 };
+
+                r = poll(pfds, sizeof(pfds) / sizeof(*pfds), -1);
+                c_assert(r >= 0);
+
+                if (state1 == -1) {
+                        r = n_acd_dispatch(acd1);
+                        c_assert(!r);
+
+                        r = n_acd_pop_event(acd1, &event);
+                        if (!r) {
+                                c_assert(event->event == N_ACD_EVENT_READY || event->event == N_ACD_EVENT_USED);
+                                state1 = !!(event->event == N_ACD_EVENT_READY);
+                        } else {
+                                c_assert(r == N_ACD_E_DONE);
+                        }
+                }
+
+                if (state2 == -1) {
+                        r = n_acd_dispatch(acd2);
+                        c_assert(!r);
+
+                        r = n_acd_pop_event(acd2, &event);
+                        if (!r) {
+                                c_assert(event->event == N_ACD_EVENT_READY || event->event == N_ACD_EVENT_USED);
+                                state2 = !!(event->event == N_ACD_EVENT_READY);
+                        } else {
+                                c_assert(r == N_ACD_E_DONE);
+                        }
+                }
+        }
+
+        n_acd_free(acd1);
+        n_acd_free(acd2);
+
+        c_assert(!state1 || !state2);
+}
+
+int main(int argc, char **argv) {
+        struct ether_addr mac1, mac2;
+        int ifindex1, ifindex2;
+
+        test_setup();
+
+        test_veth_new(&ifindex1, &mac1, &ifindex2, &mac2);
+        test_unused(ifindex1, mac1.ether_addr_octet, sizeof(mac2.ether_addr_octet), ifindex2, mac2.ether_addr_octet, sizeof(mac2.ether_addr_octet));
+
+        return 0;
+}
diff --git a/src/n-acd/src/test-unplug.c b/src/n-acd/src/test-unplug.c
new file mode 100644
index 00000000..9ad88a91
--- /dev/null
+++ b/src/n-acd/src/test-unplug.c
@@ -0,0 +1,84 @@
+/*
+ * Unplug device during test run
+ * Run the ACD engine with an address that is not used by anyone else on the
+ * link, but DOWN or UNPLUG the device while running.
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include "test.h"
+
+static void test_unplug_down(int ifindex, uint8_t *mac, size_t n_mac, unsigned int run) {
+        NAcdConfig config = {
+                .ifindex = ifindex,
+                .transport = N_ACD_TRANSPORT_ETHERNET,
+                .mac = mac,
+                .n_mac = n_mac,
+                .ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
+                .timeout_msec = 100,
+        };
+        struct pollfd pfds;
+        NAcd *acd;
+        int r, fd;
+
+        if (!run--)
+                test_veth_cmd(ifindex, "down");
+
+        r = n_acd_new(&acd);
+        c_assert(!r);
+
+        if (!run--)
+                test_veth_cmd(ifindex, "down");
+
+        n_acd_get_fd(acd, &fd);
+        r = n_acd_start(acd, &config);
+        c_assert(!r);
+
+        if (!run--)
+                test_veth_cmd(ifindex, "down");
+
+        for (;;) {
+                NAcdEvent *event;
+                pfds = (struct pollfd){ .fd = fd, .events = POLLIN };
+                r = poll(&pfds, 1, -1);
+                c_assert(r >= 0);
+
+                if (!run--)
+                        test_veth_cmd(ifindex, "down");
+
+                r = n_acd_dispatch(acd);
+                c_assert(!r);
+
+                r = n_acd_pop_event(acd, &event);
+                if (!r) {
+                        if (event->event == N_ACD_EVENT_DOWN) {
+                                break;
+                        } else {
+                                c_assert(event->event == N_ACD_EVENT_READY);
+                                test_veth_cmd(ifindex, "down");
+                        }
+                } else {
+                        c_assert(r == N_ACD_E_DONE);
+                }
+        }
+
+        n_acd_free(acd);
+}
+
+int main(int argc, char **argv) {
+        struct ether_addr mac;
+        unsigned int i;
+        int ifindex;
+
+        test_setup();
+
+        test_veth_new(&ifindex, &mac, NULL, NULL);
+
+        for (i = 0; i < 5; ++i) {
+                test_unplug_down(ifindex, mac.ether_addr_octet, sizeof(mac.ether_addr_octet), i);
+                test_veth_cmd(ifindex, "up");
+        }
+
+        return 0;
+}
diff --git a/src/n-acd/src/test-unused.c b/src/n-acd/src/test-unused.c
new file mode 100644
index 00000000..67ec2e4c
--- /dev/null
+++ b/src/n-acd/src/test-unused.c
@@ -0,0 +1,63 @@
+/*
+ * Test with unused address
+ * Run the ACD engine with an address that is not used by anyone else on the
+ * link. This should just pass through, with a short, random timeout.
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include "test.h"
+
+static void test_unused(int ifindex, const uint8_t *mac, size_t n_mac) {
+        NAcdConfig config = {
+                .ifindex = ifindex,
+                .transport = N_ACD_TRANSPORT_ETHERNET,
+                .mac = mac,
+                .n_mac = n_mac,
+                .ip = { htobe32((192 << 24) | (168 << 16) | (1 << 0)) },
+                .timeout_msec = 100,
+        };
+        struct pollfd pfds;
+        NAcd *acd;
+        int r, fd;
+
+        r = n_acd_new(&acd);
+        c_assert(!r);
+
+        n_acd_get_fd(acd, &fd);
+        r = n_acd_start(acd, &config);
+        c_assert(!r);
+
+        for (;;) {
+                NAcdEvent *event;
+                pfds = (struct pollfd){ .fd = fd, .events = POLLIN };
+                r = poll(&pfds, 1, -1);
+                c_assert(r >= 0);
+
+                r = n_acd_dispatch(acd);
+                c_assert(!r);
+
+                r = n_acd_pop_event(acd, &event);
+                if (!r) {
+                        c_assert(event->event == N_ACD_EVENT_READY);
+                        break;
+                } else {
+                        c_assert(r == N_ACD_E_DONE);
+                }
+        }
+
+        n_acd_free(acd);
+}
+
+int main(int argc, char **argv) {
+        struct ether_addr mac;
+        int ifindex;
+
+        test_setup();
+
+        test_veth_new(&ifindex, &mac, NULL, NULL);
+        test_unused(ifindex, mac.ether_addr_octet, sizeof(mac.ether_addr_octet));
+
+        return 0;
+}
diff --git a/src/n-acd/src/test-veth.c b/src/n-acd/src/test-veth.c
new file mode 100644
index 00000000..d1923683
--- /dev/null
+++ b/src/n-acd/src/test-veth.c
@@ -0,0 +1,240 @@
+/*
+ * Test on a veth link
+ *
+ * This essentially mimics a real network with two peers.
+ *
+ * Run one ACD context on each end of the tunnel. On one end probe for N,
+ * addresses on the other end pre-configure N/3 of the same addresses and probe
+ * for another N/3 of the addresses.
+ *
+ * Verify that in the case of simultaneous probes of the same address at most one
+ * succeed, in the case of probing for a configured address it always fails, and
+ * probing for a non-existent address always succeeds.
+ *
+ * Make sure to keep N fairly high as the protocol is probabilistic, and we also
+ * want to verify that resizing the internal maps works correctly.
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <stdlib.h>
+#include "test.h"
+
+#define TEST_ACD_N_PROBES (9)
+
+typedef enum {
+        TEST_ACD_STATE_UNKNOWN,
+        TEST_ACD_STATE_USED,
+        TEST_ACD_STATE_READY,
+} TestAcdState;
+
+static void test_veth(int ifindex1, uint8_t *mac1, size_t n_mac1,
+                      int ifindex2, uint8_t *mac2, size_t n_mac2) {
+        NAcdConfig *config;
+        NAcd *acd1, *acd2;
+        NAcdProbe *probes1[TEST_ACD_N_PROBES];
+        NAcdProbe *probes2[TEST_ACD_N_PROBES];
+        unsigned long state1, state2;
+        size_t n_running = 0;
+        int r;
+
+        r = n_acd_config_new(&config);
+        c_assert(!r);
+
+        n_acd_config_set_transport(config, N_ACD_TRANSPORT_ETHERNET);
+
+        n_acd_config_set_ifindex(config, ifindex1);
+        n_acd_config_set_mac(config, mac1, n_mac1);
+        r = n_acd_new(&acd1, config);
+        c_assert(!r);
+
+        n_acd_config_set_ifindex(config, ifindex2);
+        n_acd_config_set_mac(config, mac2, n_mac2);
+        r = n_acd_new(&acd2, config);
+        c_assert(!r);
+
+        n_acd_config_free(config);
+
+        {
+                NAcdProbeConfig *probe_config;
+
+                r = n_acd_probe_config_new(&probe_config);
+                c_assert(!r);
+                n_acd_probe_config_set_timeout(probe_config, 1024);
+
+                c_assert(TEST_ACD_N_PROBES <= 10 << 24);
+
+                for (size_t i = 0; i < TEST_ACD_N_PROBES; ++i) {
+                        struct in_addr ip = { htobe32((10 << 24) | i) };
+
+                        n_acd_probe_config_set_ip(probe_config, ip);
+
+                        switch (i % 3) {
+                        case 0:
+                                /*
+                                 * Probe on one side, and leave the address
+                                 * unset on the other. The probe must succeed.
+                                 */
+                                break;
+                        case 1:
+                                /*
+                                 * Preconfigure the address on one side, and
+                                 * probe on the other. The probe must fail.
+                                 */
+                                test_add_child_ip(&ip);
+                                break;
+                        case 2:
+                                /*
+                                 * Probe both sides for the same address, at
+                                 * most one may succeed.
+                                 */
+
+                                r = n_acd_probe(acd2, &probes2[i], probe_config);
+                                c_assert(!r);
+
+                                ++n_running;
+                                break;
+                        default:
+                                c_assert(0);
+                                abort();
+                                break;
+                        }
+
+                        r = n_acd_probe(acd1, &probes1[i], probe_config);
+                        c_assert(!r);
+
+                        ++n_running;
+                }
+
+                n_acd_probe_config_free(probe_config);
+
+                while (n_running > 0) {
+                        NAcdEvent *event;
+                        struct pollfd pfds[2] = {
+                                { .events = POLLIN },
+                                { .events = POLLIN },
+                        };
+
+                        n_acd_get_fd(acd1, &pfds[0].fd);
+                        n_acd_get_fd(acd2, &pfds[1].fd);
+
+                        r = poll(pfds, 2, -1);
+                        c_assert(r >= 0);
+
+                        if (pfds[0].revents & POLLIN) {
+                                r = n_acd_dispatch(acd1);
+                                c_assert(!r || r == N_ACD_E_PREEMPTED);
+
+                                for (;;) {
+                                        r = n_acd_pop_event(acd1, &event);
+                                        c_assert(!r);
+                                        if (event) {
+                                                switch (event->event) {
+                                                case N_ACD_EVENT_READY:
+                                                        n_acd_probe_get_userdata(event->ready.probe, (void**)&state1);
+                                                        c_assert(state1 == TEST_ACD_STATE_UNKNOWN);
+                                                        state1 = TEST_ACD_STATE_READY;
+                                                        n_acd_probe_set_userdata(event->ready.probe, (void*)state1);
+
+                                                        break;
+                                                case N_ACD_EVENT_USED:
+                                                        n_acd_probe_get_userdata(event->used.probe, (void**)&state1);
+                                                        c_assert(state1 == TEST_ACD_STATE_UNKNOWN);
+                                                        state1 = TEST_ACD_STATE_USED;
+                                                        n_acd_probe_set_userdata(event->used.probe, (void*)state1);
+
+                                                        break;
+                                                default:
+                                                        c_assert(0);
+                                                }
+
+                                                --n_running;
+                                        } else {
+                                                break;
+                                        }
+                                }
+                        }
+
+                        if (pfds[1].revents & POLLIN) {
+                                r = n_acd_dispatch(acd2);
+                                c_assert(!r || r == N_ACD_E_PREEMPTED);
+
+                                for (;;) {
+                                        r = n_acd_pop_event(acd2, &event);
+                                        c_assert(!r);
+                                        if (event) {
+                                                switch (event->event) {
+                                                case N_ACD_EVENT_READY:
+                                                        n_acd_probe_get_userdata(event->ready.probe, (void**)&state2);
+                                                        c_assert(state2 == TEST_ACD_STATE_UNKNOWN);
+                                                        state2 = TEST_ACD_STATE_READY;
+                                                        n_acd_probe_set_userdata(event->ready.probe, (void*)state2);
+
+                                                        break;
+                                                case N_ACD_EVENT_USED:
+                                                        n_acd_probe_get_userdata(event->used.probe, (void**)&state2);
+                                                        c_assert(state2 == TEST_ACD_STATE_UNKNOWN);
+                                                        state2 = TEST_ACD_STATE_USED;
+                                                        n_acd_probe_set_userdata(event->used.probe, (void*)state2);
+
+                                                        break;
+                                                default:
+                                                        c_assert(0);
+                                                }
+
+                                                --n_running;
+                                        } else {
+                                                break;
+                                        }
+                                }
+                        }
+                }
+
+                for (size_t i = 0; i < TEST_ACD_N_PROBES; ++i) {
+                        struct in_addr ip = { htobe32((10 << 24) | i) };
+
+                        switch (i % 3) {
+                        case 0:
+                                n_acd_probe_get_userdata(probes1[i], (void **)&state1);
+                                c_assert(state1 == TEST_ACD_STATE_READY);
+
+                                break;
+                        case 1:
+                                test_del_child_ip(&ip);
+
+                                n_acd_probe_get_userdata(probes1[i], (void **)&state1);
+                                c_assert(state1 == TEST_ACD_STATE_USED);
+
+                                break;
+                        case 2:
+                                n_acd_probe_get_userdata(probes1[i], (void **)&state1);
+                                n_acd_probe_get_userdata(probes2[i], (void **)&state2);
+                                c_assert(state1 != TEST_ACD_STATE_UNKNOWN);
+                                c_assert(state2 != TEST_ACD_STATE_UNKNOWN);
+                                c_assert(state1 == TEST_ACD_STATE_USED || state2 == TEST_ACD_STATE_USED);
+                                n_acd_probe_free(probes2[i]);
+
+                                break;
+                        }
+                        n_acd_probe_free(probes1[i]);
+                }
+        }
+
+        n_acd_unref(acd2);
+        n_acd_unref(acd1);
+}
+
+int main(int argc, char **argv) {
+        struct ether_addr mac1, mac2;
+        int ifindex1, ifindex2;
+
+        test_setup();
+
+        test_veth_new(&ifindex1, &mac1, &ifindex2, &mac2);
+        for (unsigned int i = 0; i < 8; ++i) {
+                test_veth(ifindex1, mac1.ether_addr_octet, sizeof(mac1.ether_addr_octet),
+                          ifindex2, mac2.ether_addr_octet, sizeof(mac2.ether_addr_octet));
+        }
+
+        return 0;
+}
diff --git a/src/n-acd/src/test.h b/src/n-acd/src/test.h
new file mode 100644
index 00000000..69a786a0
--- /dev/null
+++ b/src/n-acd/src/test.h
@@ -0,0 +1,213 @@
+#pragma once
+
+/*
+ * Test Helpers
+ * Bunch of helpers to setup the environment for networking tests. This
+ * includes net-namespace setups, veth setups, and more.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "n-acd.h"
+
+static inline void test_add_child_ip(const struct in_addr *addr) {
+        char *p;
+        int r;
+
+        r = asprintf(&p, "ip addr add dev veth1 %s/8", inet_ntoa(*addr));
+        c_assert(r >= 0);
+
+        r = system(p);
+        c_assert(r >= 0);
+
+        free(p);
+}
+
+static inline void test_del_child_ip(const struct in_addr *addr) {
+        char *p;
+        int r;
+
+        r = asprintf(&p, "ip addr del dev veth1 %s/8", inet_ntoa(*addr));
+        c_assert(r >= 0);
+
+        r = system(p);
+        c_assert(r >= 0);
+
+        free(p);
+}
+
+static inline void test_if_query(const char *name, int *indexp, struct ether_addr *macp) {
+        struct ifreq ifr = {};
+        size_t l;
+        int r, s;
+
+        l = strlen(name);
+        c_assert(l <= IF_NAMESIZE);
+
+        if (indexp) {
+                *indexp = if_nametoindex(name);
+                c_assert(*indexp > 0);
+        }
+
+        if (macp) {
+                s = socket(AF_INET, SOCK_DGRAM, 0);
+                c_assert(s >= 0);
+
+                strncpy(ifr.ifr_name, name, l + 1);
+                r = ioctl(s, SIOCGIFHWADDR, &ifr);
+                c_assert(r >= 0);
+
+                memcpy(macp->ether_addr_octet, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+                close(s);
+        }
+}
+
+static inline void test_veth_cmd(int ifindex, const char *cmd) {
+        char *p, name[IF_NAMESIZE + 1] = {};
+        int r;
+
+        p = if_indextoname(ifindex, name);
+        c_assert(p);
+
+        r = asprintf(&p, "ip link set %s %s", name, cmd);
+        c_assert(r >= 0);
+
+        /* Again: Ewwww... */
+        r = system(p);
+        c_assert(r == 0);
+
+        free(p);
+}
+
+static inline void test_veth_new(int *parent_indexp,
+                                 struct ether_addr *parent_macp,
+                                 int *child_indexp,
+                                 struct ether_addr *child_macp) {
+        int r;
+
+        /* Eww... but it works. */
+        r = system("ip link add type veth");
+        c_assert(r == 0);
+        r = system("ip link set veth0 up");
+        c_assert(r == 0);
+        r = system("ip link set veth1 up");
+        c_assert(r == 0);
+
+        test_if_query("veth0", parent_indexp, parent_macp);
+        test_if_query("veth1", child_indexp, child_macp);
+}
+
+static inline void test_loopback_up(int *indexp, struct ether_addr *macp) {
+        int r;
+
+        r = system("ip link set lo up");
+        c_assert(r == 0);
+
+        test_if_query("lo", indexp, macp);
+}
+
+static inline void test_raise_memlock(void) {
+        const size_t wanted = 64 * 1024 * 1024;
+        struct rlimit get, set;
+        int r;
+
+        r = getrlimit(RLIMIT_MEMLOCK, &get);
+        c_assert(!r);
+
+        /* try raising limit to @wanted */
+        set.rlim_cur = wanted;
+        set.rlim_max = (wanted > get.rlim_max) ? wanted : get.rlim_max;
+        r = setrlimit(RLIMIT_MEMLOCK, &set);
+        if (r) {
+                c_assert(errno == EPERM);
+
+                /* not privileged to raise limit, so maximize soft limit */
+                set.rlim_cur = get.rlim_max;
+                set.rlim_max = get.rlim_max;
+                r = setrlimit(RLIMIT_MEMLOCK, &set);
+                c_assert(!r);
+        }
+}
+
+static inline void test_unshare_user_namespace(void) {
+        uid_t euid;
+        gid_t egid;
+        int r, fd;
+
+        /*
+         * Enter a new user namespace as root:root.
+         */
+
+        euid = geteuid();
+        egid = getegid();
+
+        r = unshare(CLONE_NEWUSER);
+        c_assert(r >= 0);
+
+        fd = open("/proc/self/uid_map", O_WRONLY);
+        c_assert(fd >= 0);
+        r = dprintf(fd, "0 %d 1\n", euid);
+        c_assert(r >= 0);
+        close(fd);
+
+        fd = open("/proc/self/setgroups", O_WRONLY);
+        c_assert(fd >= 0);
+        r = dprintf(fd, "deny");
+        c_assert(r >= 0);
+        close(fd);
+
+        fd = open("/proc/self/gid_map", O_WRONLY);
+        c_assert(fd >= 0);
+        r = dprintf(fd, "0 %d 1\n", egid);
+        c_assert(r >= 0);
+        close(fd);
+}
+
+static inline void test_setup(void) {
+        int r;
+
+        /*
+         * Move into a new network and mount namespace both associated
+         * with a new user namespace where the current eUID is mapped to
+         * 0. Then create a private instance of /run/netns. This ensures
+         * that any network devices or network namespaces are private to
+         * the test process.
+         */
+
+        test_raise_memlock();
+        test_unshare_user_namespace();
+
+        r = unshare(CLONE_NEWNET | CLONE_NEWNS);
+        c_assert(r >= 0);
+
+        r = mount(NULL, "/", "", MS_PRIVATE | MS_REC, NULL);
+        c_assert(r >= 0);
+
+        r = mount(NULL, "/run", "tmpfs", 0, NULL);
+        c_assert(r >= 0);
+
+        r = mkdir("/run/netns", 0755);
+        c_assert(r >= 0);
+}
diff --git a/src/n-acd/src/util/test-timer.c b/src/n-acd/src/util/test-timer.c
new file mode 100644
index 00000000..a0c908bd
--- /dev/null
+++ b/src/n-acd/src/util/test-timer.c
@@ -0,0 +1,177 @@
+/*
+ * Tests for timer utility library
+ */
+
+#undef NDEBUG
+#include <c-stdaux.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/timerfd.h>
+#include "timer.h"
+
+#define N_TIMEOUTS (10000)
+
+static void test_api(void) {
+        Timer timer = TIMER_NULL(timer);
+        Timeout t1 = TIMEOUT_INIT(t1), t2 = TIMEOUT_INIT(t2), *t;
+        int r;
+
+        r = timer_init(&timer);
+        c_assert(!r);
+
+        timeout_schedule(&t1, &timer, 1);
+        timeout_schedule(&t2, &timer, 2);
+
+        r = timer_pop_timeout(&timer, 10, &t);
+        c_assert(!r);
+        c_assert(t == &t1);
+
+        timeout_unschedule(&t2);
+
+        r = timer_pop_timeout(&timer, 10, &t);
+        c_assert(!r);
+        c_assert(!t);
+
+        timer_deinit(&timer);
+}
+
+static void test_pop(void) {
+        Timer timer = TIMER_NULL(timer);
+        Timeout timeouts[N_TIMEOUTS] = {};
+        uint64_t times[N_TIMEOUTS] = {};
+        size_t n_timeouts = 0;
+        bool armed;
+        Timeout *t;
+        int r;
+
+        r = timer_init(&timer);
+        c_assert(!r);
+
+        for(size_t i = 0; i < N_TIMEOUTS; ++i) {
+                timeouts[i] = (Timeout)TIMEOUT_INIT(timeouts[i]);
+                times[i] = rand() % 128 + 1;
+                timeout_schedule(&timeouts[i], &timer, times[i]);
+        }
+
+        armed = true;
+
+        for(size_t i = 0; i <= 128; ++i) {
+                if (armed) {
+                        struct pollfd pfd = {
+                                .fd = timer.fd,
+                                .events = POLLIN,
+                        };
+                        uint64_t count;
+
+                        r = poll(&pfd, 1, -1);
+                        c_assert(r == 1);
+
+                        r = read(timer.fd, &count, sizeof(count));
+                        c_assert(r == sizeof(count));
+                        c_assert(count == 1);
+                        armed = false;
+                }
+
+                for (;;) {
+                        uint64_t current_time;
+
+                        r = timer_pop_timeout(&timer, i, &t);
+                        c_assert(!r);
+                        if (!t) {
+                                timer_rearm(&timer);
+                                break;
+                        }
+
+                        current_time = times[t - timeouts];
+                        c_assert(current_time == i);
+                        ++n_timeouts;
+                        armed = true;
+                }
+        }
+
+        c_assert(n_timeouts == N_TIMEOUTS);
+
+        r = timer_pop_timeout(&timer, (uint64_t)-1, &t);
+        c_assert(!r);
+        c_assert(!t);
+
+        timer_deinit(&timer);
+}
+
+void test_arm(void) {
+        struct itimerspec spec = {
+                .it_value = {
+                        .tv_sec = 1000,
+                },
+        };
+        int fd1, fd2, r;
+
+        fd1 = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+        c_assert(fd1 >= 0);
+
+        fd2 = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+        c_assert(fd1 >= 0);
+
+        r = timerfd_settime(fd1, 0, &spec, NULL);
+        c_assert(r >= 0);
+
+        r = timerfd_settime(fd2, 0, &spec, NULL);
+        c_assert(r >= 0);
+
+        r = timerfd_gettime(fd1, &spec);
+        c_assert(r >= 0);
+        c_assert(spec.it_value.tv_sec);
+
+        r = timerfd_gettime(fd2, &spec);
+        c_assert(r >= 0);
+        c_assert(spec.it_value.tv_sec);
+
+        spec = (struct itimerspec){};
+
+        r = timerfd_settime(fd1, 0, &spec, NULL);
+        c_assert(r >= 0);
+
+        r = timerfd_gettime(fd1, &spec);
+        c_assert(r >= 0);
+        c_assert(!spec.it_value.tv_sec);
+        c_assert(!spec.it_value.tv_nsec);
+
+        r = timerfd_gettime(fd2, &spec);
+        c_assert(r >= 0);
+        c_assert(spec.it_value.tv_sec);
+
+        spec = (struct itimerspec){ .it_value = { .tv_nsec = 1, }, };
+
+        r = timerfd_settime(fd1, 0, &spec, NULL);
+        c_assert(r >= 0);
+
+        r = poll(&(struct pollfd) { .fd = fd1, .events = POLLIN }, 1, -1);
+        c_assert(r == 1);
+
+        r = timerfd_settime(fd2, 0, &spec, NULL);
+        c_assert(r >= 0);
+
+        r = poll(&(struct pollfd) { .fd = fd2, .events = POLLIN }, 1, -1);
+        c_assert(r == 1);
+
+        spec = (struct itimerspec){};
+
+        r = timerfd_settime(fd1, 0, &spec, NULL);
+        c_assert(r >= 0);
+
+        r = poll(&(struct pollfd) { .fd = fd2, .events = POLLIN }, 1, -1);
+        c_assert(r == 1);
+
+        close(fd2);
+        close(fd1);
+}
+
+int main(int argc, char **argv) {
+        test_arm();
+        test_api();
+        test_pop();
+        return 0;
+}
diff --git a/src/n-acd/subprojects/c-list b/src/n-acd/subprojects/c-list
new file mode 120000
index 00000000..4e274698
--- /dev/null
+++ b/src/n-acd/subprojects/c-list
@@ -0,0 +1 @@
+../../c-list
\ No newline at end of file
diff --git a/src/n-acd/subprojects/c-rbtree b/src/n-acd/subprojects/c-rbtree
new file mode 120000
index 00000000..49264a87
--- /dev/null
+++ b/src/n-acd/subprojects/c-rbtree
@@ -0,0 +1 @@
+../../c-rbtree
\ No newline at end of file
diff --git a/src/n-acd/subprojects/c-siphash b/src/n-acd/subprojects/c-siphash
new file mode 120000
index 00000000..70d68818
--- /dev/null
+++ b/src/n-acd/subprojects/c-siphash
@@ -0,0 +1 @@
+../../c-siphash
\ No newline at end of file
diff --git a/src/n-acd/subprojects/libcstdaux-1 b/src/n-acd/subprojects/libcstdaux-1
new file mode 120000
index 00000000..589984f3
--- /dev/null
+++ b/src/n-acd/subprojects/libcstdaux-1
@@ -0,0 +1 @@
+../../c-stdaux
\ No newline at end of file
diff --git a/src/n-dhcp4/.editorconfig b/src/n-dhcp4/.editorconfig
new file mode 100644
index 00000000..b10bb4f3
--- /dev/null
+++ b/src/n-dhcp4/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+
+[*.{c,h}]
+indent_style = space
+indent_size = 8
diff --git a/src/n-dhcp4/.github/workflows/ci.yml b/src/n-dhcp4/.github/workflows/ci.yml
new file mode 100644
index 00000000..3583fdaa
--- /dev/null
+++ b/src/n-dhcp4/.github/workflows/ci.yml
@@ -0,0 +1,50 @@
+name: Continuous Integration
+
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron:  '0 0 * * *'
+
+jobs:
+  ci:
+    name: CI with Default Configuration
+    runs-on: ubuntu-latest
+
+    steps:
+    #
+    # Prepare CI
+    #
+    # We cannot use the github-action of the `ci-c-util` project, because we
+    # need privileges in the container. Therefore, fetch the CI sources and
+    # build the container manually.
+    #
+    - name: Fetch CI
+      uses: actions/checkout@v2
+      with:
+        repository: c-util/automation
+        ref: v1
+        path: automation
+    - name: Build CI
+      working-directory: automation/src/ci-c-util
+      run: docker build --tag ci-c-util:v1 .
+
+    #
+    # Run CI
+    #
+    # Take the CI image we built and run the CI with the default project
+    # configuration. We do not use valgrind, since it falls-over with bpf(2)
+    # syscalls.
+    #
+    - name: Fetch Sources
+      uses: actions/checkout@v2
+      with:
+        path: source
+    - name: Run through C-Util CI
+      run: |
+        docker run \
+                --privileged \
+                -v "$(pwd)/source:/github/workspace" \
+                "ci-c-util:v1" \
+                "--m32=1" \
+                "--source=/github/workspace"
diff --git a/src/n-dhcp4/.gitmodules b/src/n-dhcp4/.gitmodules
new file mode 100644
index 00000000..ffe67922
--- /dev/null
+++ b/src/n-dhcp4/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "subprojects/c-list"]
+	path = subprojects/c-list
+	url = https://github.com/c-util/c-list.git
+[submodule "subprojects/c-siphash"]
+	path = subprojects/c-siphash
+	url = https://github.com/c-util/c-siphash.git
+[submodule "subprojects/c-stdaux"]
+	path = subprojects/c-stdaux
+	url = https://github.com/c-util/c-stdaux.git
diff --git a/src/n-dhcp4/AUTHORS b/src/n-dhcp4/AUTHORS
new file mode 100644
index 00000000..b59660c5
--- /dev/null
+++ b/src/n-dhcp4/AUTHORS
@@ -0,0 +1,37 @@
+LICENSE:
+        This project is dual-licensed under both the Apache License, Version
+        2.0, and the GNU Lesser General Public License, Version 2.1+.
+
+AUTHORS-ASL:
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+                http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+
+AUTHORS-LGPL:
+        This program is free software; you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published
+        by the Free Software Foundation; either version 2.1 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public License
+        along with this program; If not, see <http://www.gnu.org/licenses/>.
+
+COPYRIGHT: (ordered alphabetically)
+        Copyright (C) 2015-2019 Red Hat, Inc.
+
+AUTHORS: (ordered alphabetically)
+        David Rheinsberg <david.rheinsberg@gmail.com>
+        Tom Gundersen <teg@jklm.no>
diff --git a/src/n-dhcp4/NEWS.md b/src/n-dhcp4/NEWS.md
new file mode 100644
index 00000000..b41de08a
--- /dev/null
+++ b/src/n-dhcp4/NEWS.md
@@ -0,0 +1,20 @@
+# n-dhcp4 - Dynamic Host Configuration Protocol for IPv4
+
+## CHANGES WITH 1:
+
+        * Initial release of n-dhcp4, an implementation of the IPv4 Dynamic
+          Host Configuration Protocol as defined in RFC-2132+.
+
+        * This is a pre-release. The code is not yet ready for production.
+          Furthermore, the server implementation is incomplete and subject to
+          change.
+          The client implementation is considered complete, but the API is not
+          set in stone. We will have to adapt it according to the needs of the
+          users. Hence, this release does not provide any ABI stability
+          guarantees, yet.
+
+        * The n-dhcp4 project is now dual-licensed: ASL-2.0 and LGPL-2.1+
+
+        Contributions from: David Rheinsberg, Tom Gundersen
+
+        - Tübingen, 2019-05-10
diff --git a/src/n-dhcp4/README.md b/src/n-dhcp4/README.md
new file mode 100644
index 00000000..298b4972
--- /dev/null
+++ b/src/n-dhcp4/README.md
@@ -0,0 +1,53 @@
+n-dhcp4
+=======
+
+Dynamic Host Configuration Protocol for IPv4
+
+The n-dhcp4 project implements the IPv4 Dynamic Host Configuration Protocol as
+defined in RFC-2132+.
+
+### Project
+
+ * **Website**: <https://nettools.github.io/n-dhcp4>
+ * **Bug Tracker**: <https://github.com/nettools/n-dhcp4/issues>
+ * **Mailing-List**: <https://groups.google.com/forum/#!forum/nettools-devel>
+
+### Requirements
+
+The requirements for this project are:
+
+ * `Linux kernel >= 3.19`
+ * `libc` (e.g., `glibc >= 2.16`)
+
+At build-time, the following software is required:
+
+ * `meson >= 0.41`
+ * `pkg-config >= 0.29`
+
+### Build
+
+The meson build-system is used for this project. Contact upstream
+documentation for detailed help. In most situations the following
+commands are sufficient to build and install from source:
+
+```sh
+mkdir build
+cd build
+meson setup ..
+ninja
+meson test
+ninja install
+```
+
+No custom configuration options are available.
+
+### Repository:
+
+ - **web**:   <https://github.com/nettools/n-dhcp4>
+ - **https**: `https://github.com/nettools/n-dhcp4.git`
+ - **ssh**:   `git@github.com:nettools/n-dhcp4.git`
+
+### License:
+
+ - **Apache-2.0** OR **LGPL-2.1-or-later**
+ - See AUTHORS file for details.
diff --git a/src/n-dhcp4/meson.build b/src/n-dhcp4/meson.build
new file mode 100644
index 00000000..8cdcb368
--- /dev/null
+++ b/src/n-dhcp4/meson.build
@@ -0,0 +1,23 @@
+project(
+        'n-dhcp4',
+        'c',
+        version: '1',
+        license: 'Apache',
+        default_options: [
+                'c_std=c11',
+        ],
+)
+project_description = 'Dynamic Host Configuration Protocol for IPv4'
+
+add_project_arguments('-D_GNU_SOURCE', language: 'c')
+mod_pkgconfig = import('pkgconfig')
+
+sub_clist = subproject('c-list')
+sub_csiphash = subproject('c-siphash')
+sub_cstdaux = subproject('libcstdaux-1')
+
+dep_clist = sub_clist.get_variable('libclist_dep')
+dep_csiphash = sub_csiphash.get_variable('libcsiphash_dep')
+dep_cstdaux = sub_cstdaux.get_variable('libcstdaux_dep')
+
+subdir('src')
diff --git a/src/n-dhcp4/src/libndhcp4.sym b/src/n-dhcp4/src/libndhcp4.sym
new file mode 100644
index 00000000..adaf6d6f
--- /dev/null
+++ b/src/n-dhcp4/src/libndhcp4.sym
@@ -0,0 +1,71 @@
+LIBNDHCP4_1 {
+global:
+        n_dhcp4_client_config_new;
+        n_dhcp4_client_config_free;
+        n_dhcp4_client_config_set_ifindex;
+        n_dhcp4_client_config_set_transport;
+        n_dhcp4_client_config_set_request_broadcast;
+        n_dhcp4_client_config_set_mac;
+        n_dhcp4_client_config_set_broadcast_mac;
+        n_dhcp4_client_config_set_client_id;
+
+        n_dhcp4_client_probe_config_new;
+        n_dhcp4_client_probe_config_free;
+        n_dhcp4_client_probe_config_set_inform_only;
+        n_dhcp4_client_probe_config_set_init_reboot;
+        n_dhcp4_client_probe_config_set_requested_ip;
+        n_dhcp4_client_probe_config_set_start_delay;
+        n_dhcp4_client_probe_config_request_option;
+        n_dhcp4_client_probe_config_append_option;
+
+        n_dhcp4_client_new;
+        n_dhcp4_client_ref;
+        n_dhcp4_client_unref;
+        n_dhcp4_client_get_fd;
+        n_dhcp4_client_dispatch;
+        n_dhcp4_client_pop_event;
+        n_dhcp4_client_update_mtu;
+        n_dhcp4_client_probe;
+        n_dhcp4_client_set_log_level;
+
+        n_dhcp4_client_probe_free;
+        n_dhcp4_client_probe_get_userdata;
+        n_dhcp4_client_probe_set_userdata;
+
+        n_dhcp4_client_lease_ref;
+        n_dhcp4_client_lease_unref;
+        n_dhcp4_client_lease_get_yiaddr;
+        n_dhcp4_client_lease_get_siaddr;
+        n_dhcp4_client_lease_get_basetime;
+        n_dhcp4_client_lease_get_lifetime;
+        n_dhcp4_client_lease_get_server_identifier;
+        n_dhcp4_client_lease_get_file;
+        n_dhcp4_client_lease_query;
+        n_dhcp4_client_lease_select;
+        n_dhcp4_client_lease_accept;
+        n_dhcp4_client_lease_decline;
+
+        n_dhcp4_server_config_new;
+        n_dhcp4_server_config_free;
+        n_dhcp4_server_config_set_ifindex;
+
+        n_dhcp4_server_new;
+        n_dhcp4_server_ref;
+        n_dhcp4_server_unref;
+        n_dhcp4_server_get_fd;
+        n_dhcp4_server_dispatch;
+        n_dhcp4_server_pop_event;
+        n_dhcp4_server_add_ip;
+
+        n_dhcp4_server_ip_free;
+
+        n_dhcp4_server_lease_ref;
+        n_dhcp4_server_lease_unref;
+        n_dhcp4_server_lease_query;
+        n_dhcp4_server_lease_append;
+        n_dhcp4_server_lease_offer;
+        n_dhcp4_server_lease_ack;
+        n_dhcp4_server_lease_nack;
+local:
+       *;
+};
diff --git a/src/n-dhcp4/src/meson.build b/src/n-dhcp4/src/meson.build
new file mode 100644
index 00000000..0a3685aa
--- /dev/null
+++ b/src/n-dhcp4/src/meson.build
@@ -0,0 +1,90 @@
+#
+# target: libndhcp4.so
+#
+
+libndhcp4_symfile = join_paths(meson.current_source_dir(), 'libndhcp4.sym')
+
+libndhcp4_deps = [
+        dep_clist,
+        dep_csiphash,
+        dep_cstdaux,
+]
+
+libndhcp4_private = static_library(
+        'ndhcp4-private',
+        [
+                'n-dhcp4-c-connection.c',
+                'n-dhcp4-c-lease.c',
+                'n-dhcp4-c-probe.c',
+                'n-dhcp4-client.c',
+                'n-dhcp4-incoming.c',
+                'n-dhcp4-outgoing.c',
+                'n-dhcp4-s-connection.c',
+                'n-dhcp4-s-lease.c',
+                'n-dhcp4-server.c',
+                'n-dhcp4-socket.c',
+                'util/link.c',
+                'util/netns.c',
+                'util/packet.c',
+                'util/socket.c',
+        ],
+        c_args: [
+                '-fvisibility=hidden',
+                '-fno-common'
+        ],
+        dependencies: libndhcp4_deps,
+        pic: true,
+)
+
+libndhcp4_shared = shared_library(
+        'ndhcp4',
+        objects: libndhcp4_private.extract_all_objects(),
+        dependencies: libndhcp4_deps,
+        install: not meson.is_subproject(),
+        soversion: 0,
+        link_depends: libndhcp4_symfile,
+        link_args: [
+                '-Wl,--version-script=@0@'.format(libndhcp4_symfile)
+        ],
+)
+
+libndhcp4_dep = declare_dependency(
+        include_directories: include_directories('.'),
+        link_with: libndhcp4_private,
+        dependencies: libndhcp4_deps,
+        version: meson.project_version(),
+)
+
+if not meson.is_subproject()
+        install_headers('n-dhcp4.h')
+
+        mod_pkgconfig.generate(
+                libraries: libndhcp4_shared,
+                version: meson.project_version(),
+                name: 'libndhcp4',
+                filebase: 'libndhcp4',
+                description: project_description,
+        )
+endif
+
+#
+# target: test-*
+#
+
+test_api = executable('test-api', ['test-api.c'], link_with: libndhcp4_shared)
+test('API Symbol Visibility', test_api)
+
+test_connection = executable('test-connection', ['test-connection.c'], dependencies: libndhcp4_dep)
+test('Connection Handling', test_connection)
+
+test_message = executable('test-message', ['test-message.c'], dependencies: libndhcp4_dep)
+test('Message Handling', test_message)
+
+test_run_client = executable('test-run-client', ['test-run-client.c'], dependencies: libndhcp4_dep)
+test('Client Runner', test_run_client, args: ['--test'])
+
+test_socket = executable('test-socket', ['test-socket.c'], dependencies: libndhcp4_dep)
+test('Socket Handling', test_socket)
+
+test_util_packet = executable('test-util-packet', ['util/test-packet.c'], dependencies: libndhcp4_dep)
+test('Packet Utility Library', test_util_packet)
diff --git a/src/n-dhcp4/src/n-dhcp4-c-connection.c b/src/n-dhcp4/src/n-dhcp4-c-connection.c
index 7cb4f23f..7024b71a 100644
--- a/src/n-dhcp4/src/n-dhcp4-c-connection.c
+++ b/src/n-dhcp4/src/n-dhcp4-c-connection.c
@@ -1031,13 +1031,13 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
         case N_DHCP4_C_MESSAGE_REBOOT:
         case N_DHCP4_C_MESSAGE_REBIND:
         case N_DHCP4_C_MESSAGE_RENEW:
+        case N_DHCP4_C_MESSAGE_DECLINE:
+        case N_DHCP4_C_MESSAGE_RELEASE:
                 request->userdata.base_time = timestamp;
                 n_dhcp4_outgoing_set_xid(request, n_dhcp4_client_probe_config_get_random(connection->probe_config));
 
                 break;
         case N_DHCP4_C_MESSAGE_SELECT:
-        case N_DHCP4_C_MESSAGE_DECLINE:
-        case N_DHCP4_C_MESSAGE_RELEASE:
                 break;
         default:
                 c_assert(0);
diff --git a/src/n-dhcp4/src/n-dhcp4-c-probe.c b/src/n-dhcp4/src/n-dhcp4-c-probe.c
index a5b38bcd..ee3a8886 100644
--- a/src/n-dhcp4/src/n-dhcp4-c-probe.c
+++ b/src/n-dhcp4/src/n-dhcp4-c-probe.c
@@ -1319,3 +1319,30 @@ int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events)
 int n_dhcp4_client_probe_update_mtu(NDhcp4ClientProbe *probe, uint16_t mtu) {
         return 0;
 }
+
+/**
+ * n_dhcp4_client_probe_release() - send a release request
+ * @probe:                          probe to operate on
+ *
+ * This sends a RELEASE message on the connection used by the probe.
+ *
+ * Return: 0 if successful otherwise non-zero value is returned.
+ */
+int n_dhcp4_client_probe_release(NDhcp4ClientProbe *probe) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_release_new(&probe->connection, &request_out, NULL);
+        if (r)
+                return r;
+
+        r = n_dhcp4_c_connection_start_request(&probe->connection, request_out, 0);
+        if (r)
+                return r;
+
+        probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
+        n_dhcp4_client_lease_unlink(probe->current_lease);
+        request_out = NULL;
+
+        return 0;
+}
diff --git a/src/n-dhcp4/src/n-dhcp4-s-connection.c b/src/n-dhcp4/src/n-dhcp4-s-connection.c
new file mode 100644
index 00000000..5ed190a8
--- /dev/null
+++ b/src/n-dhcp4/src/n-dhcp4-s-connection.c
@@ -0,0 +1,412 @@
+/*
+ * DHCPv4 Server Connection
+ *
+ * XXX
+ */
+
+#include <assert.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <net/if_arp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include "n-dhcp4-private.h"
+#include "util/packet.h"
+
+int n_dhcp4_s_connection_init(NDhcp4SConnection *connection, int ifindex) {
+        int r;
+
+        *connection = (NDhcp4SConnection)N_DHCP4_S_CONNECTION_NULL(*connection);
+
+        r = n_dhcp4_s_socket_packet_new(&connection->fd_packet);
+        if (r)
+                return r;
+
+        r = n_dhcp4_s_socket_udp_new(&connection->fd_udp, ifindex);
+        if (r)
+                return r;
+
+        connection->ifindex = ifindex;
+
+        return 0;
+}
+
+void n_dhcp4_s_connection_deinit(NDhcp4SConnection *connection) {
+        c_assert(!connection->ip);
+
+        if (connection->fd_udp >= 0) {
+                close(connection->fd_udp);
+        }
+
+        if (connection->fd_packet >= 0) {
+                close(connection->fd_packet);
+        }
+
+        *connection = (NDhcp4SConnection)N_DHCP4_S_CONNECTION_NULL(*connection);
+}
+
+void n_dhcp4_s_connection_get_fd(NDhcp4SConnection *connection, int *fdp) {
+        *fdp = connection->fd_udp;
+}
+
+static bool n_dhcp4_s_connection_owns_ip(NDhcp4SConnection *connection, struct in_addr addr) {
+        if (!connection->ip)
+                return false;
+        return (connection->ip->ip.s_addr == addr.s_addr);
+}
+
+static int n_dhcp4_s_connection_verify_incoming(NDhcp4SConnection *connection,
+                                                NDhcp4Incoming *message,
+                                                bool broadcast) {
+        uint8_t type;
+        int r;
+
+        r = n_dhcp4_incoming_query_message_type(message, &type);
+        if (r) {
+                if (r == N_DHCP4_E_UNSET)
+                        return N_DHCP4_E_MALFORMED;
+                else
+                        return r;
+        }
+
+        switch (type) {
+        case N_DHCP4_MESSAGE_DISCOVER:
+                message->userdata.type = N_DHCP4_C_MESSAGE_DISCOVER;
+                break;
+        case N_DHCP4_MESSAGE_REQUEST: {
+                struct in_addr server_identifier = {};
+                struct in_addr requested_ip = {};
+
+                r = n_dhcp4_incoming_query_server_identifier(message, &server_identifier);
+                if (r) {
+                        if (r == N_DHCP4_E_UNSET) {
+                                r = n_dhcp4_incoming_query_requested_ip(message, &requested_ip);
+                                if (r) {
+                                        if (r == N_DHCP4_E_UNSET) {
+                                                if (broadcast) {
+                                                        message->userdata.type = N_DHCP4_C_MESSAGE_REBIND;
+                                                } else {
+                                                        message->userdata.type = N_DHCP4_C_MESSAGE_RENEW;
+                                                }
+                                        } else {
+                                                return r;
+                                        }
+                                } else {
+                                        message->userdata.type = N_DHCP4_C_MESSAGE_REBOOT;
+                                }
+                        } else {
+                                return r;
+                        }
+                } else {
+                        if (n_dhcp4_s_connection_owns_ip(connection, server_identifier)) {
+                                message->userdata.type = N_DHCP4_C_MESSAGE_SELECT;
+                        } else {
+                                message->userdata.type = N_DHCP4_C_MESSAGE_IGNORE;
+                        }
+                }
+        }
+                break;
+        case N_DHCP4_MESSAGE_DECLINE:
+                message->userdata.type = N_DHCP4_C_MESSAGE_DECLINE;
+                break;
+        case N_DHCP4_MESSAGE_RELEASE:
+                message->userdata.type = N_DHCP4_C_MESSAGE_RELEASE;
+                break;
+        default:
+                return N_DHCP4_E_UNEXPECTED;
+        }
+
+        return 0;
+}
+
+int n_dhcp4_s_connection_dispatch_io(NDhcp4SConnection *connection, NDhcp4Incoming **messagep) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
+        struct sockaddr_in dest = {};
+        int r;
+
+        r = n_dhcp4_s_socket_udp_recv(connection->fd_udp,
+                                          connection->buf,
+                                          sizeof(connection->buf),
+                                          &message,
+                                          &dest);
+        if (r)
+                return r;
+
+        r = n_dhcp4_s_connection_verify_incoming(connection,
+                                                 message,
+                                                 dest.sin_addr.s_addr == INADDR_BROADCAST);
+        if (r) {
+                if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) {
+                        *messagep = NULL;
+                        return 0;
+                }
+
+                return -ENOTRECOVERABLE;
+        }
+
+        *messagep = message;
+        message = NULL;
+        return 0;
+}
+
+/*
+ * If the 'giaddr' field in a DHCP message from a client is non-zero,
+ * the server sends any return messages to the 'DHCP server' port on the
+ * BOOTP relay agent whose address appears in 'giaddr'. If the 'giaddr'
+ * field is zero and the 'ciaddr' field is nonzero, then the server
+ * unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
+ * If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
+ * set, then the server broadcasts DHCPOFFER and DHCPACK messages to
+ * 0xffffffff. If the broadcast bit is not set and 'giaddr' is zero and
+ * 'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
+ * messages to the client's hardware address and 'yiaddr' address.  In
+ * all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
+ * messages to 0xffffffff.
+ */
+int n_dhcp4_s_connection_send_reply(NDhcp4SConnection *connection,
+                                    const struct in_addr *server_addr,
+                                    NDhcp4Outgoing *message) {
+        NDhcp4Header *header = n_dhcp4_outgoing_get_header(message);
+        int r;
+
+        if (header->giaddr) {
+                const struct in_addr giaddr = { header->giaddr };
+
+                r = n_dhcp4_s_socket_udp_send(connection->fd_udp,
+                                              server_addr,
+                                              &giaddr,
+                                              message);
+                if (r)
+                        return r;
+        } else if (header->ciaddr) {
+                const struct in_addr ciaddr = { header->ciaddr };
+
+                r = n_dhcp4_s_socket_udp_send(connection->fd_udp,
+                                              server_addr,
+                                              &ciaddr,
+                                              message);
+                if (r)
+                        return r;
+        } else if (header->flags & htons(N_DHCP4_MESSAGE_FLAG_BROADCAST)) {
+                r = n_dhcp4_s_socket_udp_broadcast(connection->fd_udp,
+                                                   server_addr,
+                                                   message);
+                if (r)
+                        return r;
+        } else {
+                r = n_dhcp4_s_socket_packet_send(connection->fd_packet,
+                                                 connection->ifindex,
+                                                 server_addr,
+                                                 header->chaddr,
+                                                 header->hlen,
+                                                 &(struct in_addr){header->yiaddr},
+                                                 N_DHCP4_DSCP_DEFAULT,
+                                                 message);
+                if (r)
+                        return r;
+        }
+
+        return 0;
+}
+
+static void n_dhcp4_s_connection_init_reply_header(NDhcp4SConnection *connection,
+                                                   NDhcp4Header *request,
+                                                   NDhcp4Header *reply) {
+        reply->op = N_DHCP4_OP_BOOTREPLY;
+
+        reply->htype = request->htype;
+        reply->hlen = request->hlen;
+        reply->flags = request->flags;
+        reply->xid = request->xid;
+        reply->ciaddr = request->ciaddr;
+        reply->giaddr = request->giaddr;
+        memcpy(reply->chaddr, request->chaddr, request->hlen);
+}
+
+static int n_dhcp4_s_connection_outgoing_set_yiaddr(NDhcp4Outgoing *message,
+                                                     uint32_t yiaddr,
+                                                     uint32_t lifetime) {
+        uint32_t t1 = lifetime / 2;
+        uint32_t t2 = ((uint64_t)lifetime * 7) / 8;
+        struct in_addr addr = { .s_addr = yiaddr };
+        int r;
+
+        r = n_dhcp4_outgoing_append_lifetime(message, lifetime);
+        if (r)
+                return r;
+
+        r = n_dhcp4_outgoing_append_t1(message, t1);
+        if (r)
+                return r;
+
+        r = n_dhcp4_outgoing_append_t2(message, t2);
+        if (r)
+                return r;
+
+        n_dhcp4_outgoing_set_yiaddr(message, addr);
+
+        return 0;
+}
+
+static int n_dhcp4_s_connection_new_reply(NDhcp4SConnection *connection,
+                                          NDhcp4Outgoing **messagep,
+                                          NDhcp4Incoming *request,
+                                          uint8_t type,
+                                          const struct in_addr *server_address) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *message = NULL;
+        uint16_t max_message_size;
+        uint8_t *client_identifier;
+        size_t n_client_identifier;
+        int r;
+
+        r = n_dhcp4_incoming_query_max_message_size(request, &max_message_size);
+        if (r)
+                return r;
+
+        r = n_dhcp4_outgoing_new(&message,
+                                 max_message_size,
+                                 N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME);
+        if (r)
+                return r;
+
+        n_dhcp4_s_connection_init_reply_header(connection,
+                                               n_dhcp4_incoming_get_header(request),
+                                               n_dhcp4_outgoing_get_header(message));
+
+        r = n_dhcp4_outgoing_append(message, N_DHCP4_OPTION_MESSAGE_TYPE, &type, sizeof(type));
+        if (r)
+                return r;
+
+        r = n_dhcp4_outgoing_append_server_identifier(message, *server_address);
+        if (r)
+                return r;
+
+        r = n_dhcp4_incoming_query(request,
+                                   N_DHCP4_OPTION_CLIENT_IDENTIFIER,
+                                   &client_identifier,
+                                   &n_client_identifier);
+        if (!r) {
+                r = n_dhcp4_outgoing_append(message,
+                                            N_DHCP4_OPTION_CLIENT_IDENTIFIER,
+                                            client_identifier,
+                                            n_client_identifier);
+                if (r)
+                        return r;
+        } else if (r != N_DHCP4_E_UNSET) {
+                return r;
+        }
+
+        *messagep = message;
+        message = NULL;
+        return 0;
+}
+
+int n_dhcp4_s_connection_offer_new(NDhcp4SConnection *connection,
+                                   NDhcp4Outgoing **replyp,
+                                   NDhcp4Incoming *request,
+                                   const struct in_addr *server_address,
+                                   const struct in_addr *client_address,
+                                   uint32_t lifetime) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_s_connection_new_reply(connection,
+                                           &reply,
+                                           request,
+                                           N_DHCP4_MESSAGE_OFFER,
+                                           server_address);
+        if (r)
+                return r;
+
+        r = n_dhcp4_s_connection_outgoing_set_yiaddr(reply,
+                                                     client_address->s_addr,
+                                                     lifetime);
+        if (r)
+                return r;
+
+        *replyp = reply;
+        reply = NULL;
+        return 0;
+}
+
+int n_dhcp4_s_connection_ack_new(NDhcp4SConnection *connection,
+                                 NDhcp4Outgoing **replyp,
+                                 NDhcp4Incoming *request,
+                                 const struct in_addr *server_address,
+                                 const struct in_addr *client_address,
+                                 uint32_t lifetime) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_s_connection_new_reply(connection,
+                                           &reply,
+                                           request,
+                                           N_DHCP4_MESSAGE_ACK,
+                                           server_address);
+        if (r)
+                return r;
+
+        r = n_dhcp4_s_connection_outgoing_set_yiaddr(reply,
+                                                     client_address->s_addr,
+                                                     lifetime);
+        if (r)
+                return r;
+
+        *replyp = reply;
+        reply = NULL;
+        return 0;
+}
+
+int n_dhcp4_s_connection_nak_new(NDhcp4SConnection *connection,
+                                 NDhcp4Outgoing **replyp,
+                                 NDhcp4Incoming *request,
+                                 const struct in_addr *server_address) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_s_connection_new_reply(connection,
+                                           &reply,
+                                           request,
+                                           N_DHCP4_MESSAGE_NAK,
+                                           server_address);
+        if (r)
+                return r;
+
+        /*
+         * The RFC is a bit unclear on how NAK should be sent, on the
+         * one hand it says that they should be unconditionally broadcast
+         * (unless going through a relay agent), on the other, when they
+         * do go through a relay agent, they will not be. We treat them
+         * as any other reply and only broadcast when the broadcast bit
+         * is set.
+         */
+
+        *replyp = reply;
+        reply = NULL;
+        return 0;
+}
+
+void n_dhcp4_s_connection_ip_init(NDhcp4SConnectionIp *ip, struct in_addr addr) {
+        *ip = (NDhcp4SConnectionIp)N_DHCP4_S_CONNECTION_IP_NULL(*ip);
+        ip->ip = addr;
+}
+
+void n_dhcp4_s_connection_ip_deinit(NDhcp4SConnectionIp *ip) {
+        c_assert(!ip->connection);
+        *ip = (NDhcp4SConnectionIp)N_DHCP4_S_CONNECTION_IP_NULL(*ip);
+}
+
+void n_dhcp4_s_connection_ip_link(NDhcp4SConnectionIp *ip, NDhcp4SConnection *connection) {
+        c_assert(!connection->ip);
+        c_assert(!ip->connection);
+
+        connection->ip = ip;
+        ip->connection = connection;
+}
+
+void n_dhcp4_s_connection_ip_unlink(NDhcp4SConnectionIp *ip) {
+        ip->connection->ip = NULL;
+        ip->connection = NULL;
+}
diff --git a/src/n-dhcp4/src/n-dhcp4-s-lease.c b/src/n-dhcp4/src/n-dhcp4-s-lease.c
new file mode 100644
index 00000000..cb8aabf9
--- /dev/null
+++ b/src/n-dhcp4/src/n-dhcp4-s-lease.c
@@ -0,0 +1,103 @@
+/*
+ * XXX
+ */
+
+#include <assert.h>
+#include <c-list.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "n-dhcp4.h"
+#include "n-dhcp4-private.h"
+
+/**
+ * n_dhcp4_server_lease_new() - XXX
+ */
+int n_dhcp4_server_lease_new(NDhcp4ServerLease **leasep, NDhcp4Incoming *message) {
+        _c_cleanup_(n_dhcp4_server_lease_unrefp) NDhcp4ServerLease *lease = NULL;
+
+        c_assert(leasep);
+
+        lease = malloc(sizeof(*lease));
+        if (!lease)
+                return -ENOMEM;
+
+        *lease = (NDhcp4ServerLease)N_DHCP4_SERVER_LEASE_NULL(*lease);
+
+        lease->request = message;
+
+        *leasep = lease;
+        lease = NULL;
+        return 0;
+}
+
+static void n_dhcp4_server_lease_free(NDhcp4ServerLease *lease) {
+        c_assert(!lease->server);
+
+        c_list_unlink(&lease->server_link);
+
+        n_dhcp4_incoming_free(lease->request);
+        free(lease);
+}
+
+/**
+ * n_dhcp4_server_lease_ref() - XXX
+ */
+_c_public_ NDhcp4ServerLease *n_dhcp4_server_lease_ref(NDhcp4ServerLease *lease) {
+        if (lease)
+                ++lease->n_refs;
+        return lease;
+}
+
+/**
+ * n_dhcp4_server_lease_unref() - XXX
+ */
+_c_public_ NDhcp4ServerLease *n_dhcp4_server_lease_unref(NDhcp4ServerLease *lease) {
+        if (lease && !--lease->n_refs)
+                n_dhcp4_server_lease_free(lease);
+        return NULL;
+}
+
+/**
+ * n_dhcp4_server_lease_query() - XXX
+ */
+_c_public_ int n_dhcp4_server_lease_query(NDhcp4ServerLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap) {
+        switch (option) {
+        case N_DHCP4_OPTION_PAD:
+        case N_DHCP4_OPTION_REQUESTED_IP_ADDRESS:
+        case N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME:
+        case N_DHCP4_OPTION_OVERLOAD:
+        case N_DHCP4_OPTION_MESSAGE_TYPE:
+        case N_DHCP4_OPTION_SERVER_IDENTIFIER:
+        case N_DHCP4_OPTION_PARAMETER_REQUEST_LIST:
+        case N_DHCP4_OPTION_ERROR_MESSAGE:
+        case N_DHCP4_OPTION_MAXIMUM_MESSAGE_SIZE:
+        case N_DHCP4_OPTION_RENEWAL_T1_TIME:
+        case N_DHCP4_OPTION_REBINDING_T2_TIME:
+        case N_DHCP4_OPTION_END:
+                return N_DHCP4_E_INTERNAL;
+        }
+
+        return n_dhcp4_incoming_query(lease->request, option, datap, n_datap);
+}
+
+_c_public_ int n_dhcp4_server_lease_append(NDhcp4ServerLease *lease, uint8_t option, uint8_t *data, size_t n_data) {
+        /* XXX */
+        return -ENOTRECOVERABLE;
+}
+
+_c_public_ int n_dhcp4_server_lease_offer(NDhcp4ServerLease *lease) {
+        /* XXX */
+        return -ENOTRECOVERABLE;
+}
+
+_c_public_ int n_dhcp4_server_lease_ack(NDhcp4ServerLease *lease) {
+        /* XXX */
+        return -ENOTRECOVERABLE;
+}
+
+_c_public_ int n_dhcp4_server_lease_nack(NDhcp4ServerLease *lease) {
+        /* XXX */
+        return -ENOTRECOVERABLE;
+}
diff --git a/src/n-dhcp4/src/n-dhcp4-server.c b/src/n-dhcp4/src/n-dhcp4-server.c
new file mode 100644
index 00000000..7b971eeb
--- /dev/null
+++ b/src/n-dhcp4/src/n-dhcp4-server.c
@@ -0,0 +1,241 @@
+/*
+ * Server Side of the Dynamic Host Configuration Protocol for IPv4
+ *
+ * XXX
+ */
+
+#include <assert.h>
+#include <c-list.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/time.h>
+#include <sys/timerfd.h>
+#include <time.h>
+#include <unistd.h>
+#include "n-dhcp4.h"
+#include "n-dhcp4-private.h"
+#include "util/packet.h"
+
+/**
+ * n_dhcp4_server_config_new() - XXX
+ */
+_c_public_ int n_dhcp4_server_config_new(NDhcp4ServerConfig **configp) {
+        _c_cleanup_(n_dhcp4_server_config_freep) NDhcp4ServerConfig *config = NULL;
+
+        config = calloc(1, sizeof(*config));
+        if (!config)
+                return -ENOMEM;
+
+        *config = (NDhcp4ServerConfig)N_DHCP4_SERVER_CONFIG_NULL(*config);
+
+        *configp = config;
+        config = NULL;
+        return 0;
+}
+
+/**
+ * n_dhcp4_server_config_free() - XXX
+ */
+_c_public_ NDhcp4ServerConfig *n_dhcp4_server_config_free(NDhcp4ServerConfig *config) {
+        if (!config)
+                return NULL;
+
+        free(config);
+
+        return NULL;
+}
+
+/**
+ * n_dhcp4_server_config_set_ifindex() - XXX
+ */
+_c_public_ void n_dhcp4_server_config_set_ifindex(NDhcp4ServerConfig *config, int ifindex) {
+        config->ifindex = ifindex;
+}
+
+/**
+ * n_dhcp4_s_event_node_new() - XXX
+ */
+int n_dhcp4_s_event_node_new(NDhcp4SEventNode **nodep) {
+        NDhcp4SEventNode *node;
+
+        node = calloc(1, sizeof(*node));
+        if (!node)
+                return -ENOMEM;
+
+        *node = (NDhcp4SEventNode)N_DHCP4_S_EVENT_NODE_NULL(*node);
+
+        *nodep = node;
+        return 0;
+}
+
+/**
+ * n_dhcp4_s_event_node_free() - XXX
+ */
+NDhcp4SEventNode *n_dhcp4_s_event_node_free(NDhcp4SEventNode *node) {
+        if (!node)
+                return NULL;
+
+        c_list_unlink(&node->server_link);
+        free(node);
+
+        return NULL;
+}
+
+/**
+ * n_dhcp4_server_new() - XXX
+ */
+_c_public_ int n_dhcp4_server_new(NDhcp4Server **serverp, NDhcp4ServerConfig *config) {
+        _c_cleanup_(n_dhcp4_server_unrefp) NDhcp4Server *server = NULL;
+        int r;
+
+        c_assert(serverp);
+
+        server = malloc(sizeof(*server));
+        if (!server)
+                return -ENOMEM;
+
+        *server = (NDhcp4Server)N_DHCP4_SERVER_NULL(*server);
+
+        r = n_dhcp4_s_connection_init(&server->connection, config->ifindex);
+        if (r)
+                return r;
+
+        *serverp = server;
+        server = NULL;
+        return 0;
+}
+
+static void n_dhcp4_server_free(NDhcp4Server *server) {
+        NDhcp4SEventNode *node, *t_node;
+
+        c_list_for_each_entry_safe(node, t_node, &server->event_list, server_link)
+                n_dhcp4_s_event_node_free(node);
+
+        free(server);
+}
+
+/**
+ * n_dhcp4_server_ref() - XXX
+ */
+_c_public_ NDhcp4Server *n_dhcp4_server_ref(NDhcp4Server *server) {
+        if (server)
+                ++server->n_refs;
+        return server;
+}
+
+/**
+ * n_dhcp4_server_unref() - XXX
+ */
+_c_public_ NDhcp4Server *n_dhcp4_server_unref(NDhcp4Server *server) {
+        if (server && !--server->n_refs)
+                n_dhcp4_server_free(server);
+        return NULL;
+}
+
+/**
+ * n_dhcp4_server_raise() - XXX
+ */
+int n_dhcp4_server_raise(NDhcp4Server *server, NDhcp4SEventNode **nodep, unsigned int event) {
+        NDhcp4SEventNode *node;
+        int r;
+
+        r = n_dhcp4_s_event_node_new(&node);
+        if (r)
+                return r;
+
+        node->event.event = event;
+        c_list_link_tail(&server->event_list, &node->server_link);
+
+        if (nodep)
+                *nodep = node;
+        return 0;
+}
+
+/**
+ * n_dhcp4_server_get_fd() - XXX
+ */
+_c_public_ void n_dhcp4_server_get_fd(NDhcp4Server *server, int *fdp) {
+        n_dhcp4_s_connection_get_fd(&server->connection, fdp);
+}
+
+/**
+ * n_dhcp4_server_dispatch() - XXX
+ */
+_c_public_ int n_dhcp4_server_dispatch(NDhcp4Server *server) {
+        int r;
+
+        for (unsigned int i = 0; i < 128; ++i) {
+                _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
+
+                r = n_dhcp4_s_connection_dispatch_io(&server->connection, &message);
+                if (r) {
+                        if (r == N_DHCP4_E_AGAIN)
+                                return 0;
+                        return r;
+                }
+        }
+
+        return N_DHCP4_E_PREEMPTED;
+}
+
+/**
+ * n_dhcp4_server_pop_event() - XXX
+ */
+_c_public_ int n_dhcp4_server_pop_event(NDhcp4Server *server, NDhcp4ServerEvent **eventp) {
+        NDhcp4SEventNode *node, *t_node;
+
+        c_list_for_each_entry_safe(node, t_node, &server->event_list, server_link) {
+                if (node->is_public) {
+                        n_dhcp4_s_event_node_free(node);
+                        continue;
+                }
+
+                node->is_public = true;
+                *eventp = &node->event;
+                return 0;
+        }
+
+        *eventp = NULL;
+        return 0;
+}
+
+/**
+ * n_dhcp4_server_add_ip() - XXX
+ */
+_c_public_ int n_dhcp4_server_add_ip(NDhcp4Server *server, NDhcp4ServerIp **ipp, struct in_addr addr) {
+        _c_cleanup_(n_dhcp4_server_ip_freep) NDhcp4ServerIp *ip = NULL;
+
+        /* XXX: support more than one address */
+        if (server->connection.ip)
+                return -EBUSY;
+
+        ip = malloc(sizeof(*ip));
+        if (!ip)
+                return -ENOMEM;
+
+        *ip = (NDhcp4ServerIp)N_DHCP4_SERVER_IP_NULL(*ip);
+
+        n_dhcp4_s_connection_ip_init(&ip->ip, addr);
+        n_dhcp4_s_connection_ip_link(&ip->ip, &server->connection);
+
+        *ipp = ip;
+        ip = NULL;
+        return 0;
+}
+
+/**
+ * n_dhcp4_server_ip_free() - XXX
+ */
+_c_public_ NDhcp4ServerIp *n_dhcp4_server_ip_free(NDhcp4ServerIp *ip) {
+        if (!ip)
+                return NULL;
+
+        n_dhcp4_s_connection_ip_unlink(&ip->ip);
+        n_dhcp4_s_connection_ip_deinit(&ip->ip);
+
+        free(ip);
+        return NULL;
+}
diff --git a/src/n-dhcp4/src/n-dhcp4.h b/src/n-dhcp4/src/n-dhcp4.h
index f6c87a8d..f3cf4210 100644
--- a/src/n-dhcp4/src/n-dhcp4.h
+++ b/src/n-dhcp4/src/n-dhcp4.h
@@ -161,6 +161,7 @@ NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe);
 
 void n_dhcp4_client_probe_set_userdata(NDhcp4ClientProbe *probe, void *userdata);
 void n_dhcp4_client_probe_get_userdata(NDhcp4ClientProbe *probe, void **userdatap);
+int n_dhcp4_client_probe_release(NDhcp4ClientProbe *probe);
 
 /* client leases */
 
diff --git a/src/n-dhcp4/src/test-api.c b/src/n-dhcp4/src/test-api.c
new file mode 100644
index 00000000..fac33008
--- /dev/null
+++ b/src/n-dhcp4/src/test-api.c
@@ -0,0 +1,157 @@
+/*
+ * API Visibility Tests
+ * This verifies the visibility and availability of the exported API.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <stdlib.h>
+#include "n-dhcp4.h"
+
+static void test_api_constants(void) {
+        assert(1 + N_DHCP4_CLIENT_START_DELAY_RFC2131);
+
+        assert(1 + _N_DHCP4_E_SUCCESS);
+        assert(1 + N_DHCP4_E_PREEMPTED);
+        assert(1 + N_DHCP4_E_INTERNAL);
+        assert(1 + N_DHCP4_E_INVALID_IFINDEX);
+        assert(1 + N_DHCP4_E_INVALID_TRANSPORT);
+        assert(1 + N_DHCP4_E_INVALID_ADDRESS);
+        assert(1 + N_DHCP4_E_INVALID_CLIENT_ID);
+        assert(1 + N_DHCP4_E_DUPLICATE_OPTION);
+        assert(1 + N_DHCP4_E_UNSET);
+        assert(1 + _N_DHCP4_E_N);
+
+        assert(1 + N_DHCP4_TRANSPORT_ETHERNET);
+        assert(1 + N_DHCP4_TRANSPORT_INFINIBAND);
+        assert(1 + _N_DHCP4_TRANSPORT_N);
+
+        assert(1 + N_DHCP4_CLIENT_EVENT_DOWN);
+        assert(1 + N_DHCP4_CLIENT_EVENT_OFFER);
+        assert(1 + N_DHCP4_CLIENT_EVENT_GRANTED);
+        assert(1 + N_DHCP4_CLIENT_EVENT_RETRACTED);
+        assert(1 + N_DHCP4_CLIENT_EVENT_EXTENDED);
+        assert(1 + N_DHCP4_CLIENT_EVENT_EXPIRED);
+        assert(1 + N_DHCP4_CLIENT_EVENT_CANCELLED);
+        assert(1 + _N_DHCP4_CLIENT_EVENT_N);
+
+        assert(1 + N_DHCP4_SERVER_EVENT_DOWN);
+        assert(1 + N_DHCP4_SERVER_EVENT_DISCOVER);
+        assert(1 + N_DHCP4_SERVER_EVENT_REQUEST);
+        assert(1 + N_DHCP4_SERVER_EVENT_RENEW);
+        assert(1 + N_DHCP4_SERVER_EVENT_DECLINE);
+        assert(1 + N_DHCP4_SERVER_EVENT_RELEASE);
+        assert(1 + _N_DHCP4_SERVER_EVENT_N);
+}
+
+static void test_api_types(void) {
+        assert(sizeof(NDhcp4ClientConfig*) > 0);
+        assert(sizeof(NDhcp4ClientProbeConfig*) > 0);
+        assert(sizeof(NDhcp4Client*) > 0);
+        assert(sizeof(NDhcp4ClientEvent) > 0);
+        assert(sizeof(NDhcp4ClientProbe*) > 0);
+        assert(sizeof(NDhcp4ClientLease*) > 0);
+        assert(sizeof(NDhcp4Server*) > 0);
+        assert(sizeof(NDhcp4ServerConfig*) > 0);
+        assert(sizeof(NDhcp4ServerEvent) > 0);
+        assert(sizeof(NDhcp4ServerIp*) > 0);
+        assert(sizeof(NDhcp4ServerLease*) > 0);
+}
+
+static void test_api_functions(void) {
+        void *fns[] = {
+                (void *)n_dhcp4_client_config_new,
+                (void *)n_dhcp4_client_config_free,
+                (void *)n_dhcp4_client_config_freep,
+                (void *)n_dhcp4_client_config_freev,
+                (void *)n_dhcp4_client_config_set_ifindex,
+                (void *)n_dhcp4_client_config_set_transport,
+                (void *)n_dhcp4_client_config_set_request_broadcast,
+                (void *)n_dhcp4_client_config_set_mac,
+                (void *)n_dhcp4_client_config_set_broadcast_mac,
+                (void *)n_dhcp4_client_config_set_client_id,
+
+                (void *)n_dhcp4_client_probe_config_new,
+                (void *)n_dhcp4_client_probe_config_free,
+                (void *)n_dhcp4_client_probe_config_freep,
+                (void *)n_dhcp4_client_probe_config_freev,
+                (void *)n_dhcp4_client_probe_config_set_inform_only,
+                (void *)n_dhcp4_client_probe_config_set_init_reboot,
+                (void *)n_dhcp4_client_probe_config_set_requested_ip,
+                (void *)n_dhcp4_client_probe_config_set_start_delay,
+                (void *)n_dhcp4_client_probe_config_request_option,
+                (void *)n_dhcp4_client_probe_config_append_option,
+
+                (void *)n_dhcp4_client_new,
+                (void *)n_dhcp4_client_ref,
+                (void *)n_dhcp4_client_unref,
+                (void *)n_dhcp4_client_unrefp,
+                (void *)n_dhcp4_client_unrefv,
+                (void *)n_dhcp4_client_get_fd,
+                (void *)n_dhcp4_client_dispatch,
+                (void *)n_dhcp4_client_pop_event,
+                (void *)n_dhcp4_client_update_mtu,
+                (void *)n_dhcp4_client_probe,
+
+                (void *)n_dhcp4_client_probe_free,
+                (void *)n_dhcp4_client_probe_freep,
+                (void *)n_dhcp4_client_probe_freev,
+                (void *)n_dhcp4_client_probe_get_userdata,
+                (void *)n_dhcp4_client_probe_set_userdata,
+
+                (void *)n_dhcp4_client_lease_ref,
+                (void *)n_dhcp4_client_lease_unref,
+                (void *)n_dhcp4_client_lease_unrefp,
+                (void *)n_dhcp4_client_lease_unrefv,
+                (void *)n_dhcp4_client_lease_get_yiaddr,
+                (void *)n_dhcp4_client_lease_get_siaddr,
+                (void *)n_dhcp4_client_lease_get_lifetime,
+                (void *)n_dhcp4_client_lease_get_server_identifier,
+                (void *)n_dhcp4_client_lease_get_file,
+                (void *)n_dhcp4_client_lease_query,
+                (void *)n_dhcp4_client_lease_select,
+                (void *)n_dhcp4_client_lease_accept,
+                (void *)n_dhcp4_client_lease_decline,
+
+                (void *)n_dhcp4_server_config_new,
+                (void *)n_dhcp4_server_config_free,
+                (void *)n_dhcp4_server_config_freep,
+                (void *)n_dhcp4_server_config_freev,
+                (void *)n_dhcp4_server_config_set_ifindex,
+
+                (void *)n_dhcp4_server_new,
+                (void *)n_dhcp4_server_ref,
+                (void *)n_dhcp4_server_unref,
+                (void *)n_dhcp4_server_unrefp,
+                (void *)n_dhcp4_server_unrefv,
+                (void *)n_dhcp4_server_get_fd,
+                (void *)n_dhcp4_server_dispatch,
+                (void *)n_dhcp4_server_pop_event,
+                (void *)n_dhcp4_server_add_ip,
+
+                (void *)n_dhcp4_server_ip_free,
+                (void *)n_dhcp4_server_ip_freep,
+                (void *)n_dhcp4_server_ip_freev,
+
+                (void *)n_dhcp4_server_lease_ref,
+                (void *)n_dhcp4_server_lease_unref,
+                (void *)n_dhcp4_server_lease_unrefp,
+                (void *)n_dhcp4_server_lease_unrefv,
+                (void *)n_dhcp4_server_lease_query,
+                (void *)n_dhcp4_server_lease_append,
+                (void *)n_dhcp4_server_lease_offer,
+                (void *)n_dhcp4_server_lease_ack,
+                (void *)n_dhcp4_server_lease_nack,
+        };
+        size_t i;
+
+        for (i = 0; i < sizeof(fns) / sizeof(*fns); ++i)
+                assert(!!fns[i]);
+}
+
+int main(int argc, char **argv) {
+        test_api_constants();
+        test_api_types();
+        test_api_functions();
+        return 0;
+}
diff --git a/src/n-dhcp4/src/test-connection.c b/src/n-dhcp4/src/test-connection.c
new file mode 100644
index 00000000..98bd2aca
--- /dev/null
+++ b/src/n-dhcp4/src/test-connection.c
@@ -0,0 +1,390 @@
+/*
+ * Tests for DHCP4 Client Connections
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <endian.h>
+#include <errno.h>
+#include <poll.h>
+#include <linux/if_packet.h>
+#include <net/if_arp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include "n-dhcp4-private.h"
+#include "test.h"
+#include "util/link.h"
+#include "util/netns.h"
+#include "util/packet.h"
+
+static void test_poll_client(int efd, unsigned int u32) {
+        struct epoll_event event = {};
+        int r;
+
+        r = epoll_wait(efd, &event, 1, -1);
+        c_assert(r == 1);
+        c_assert(event.events == EPOLLIN);
+        c_assert(event.data.u32 == u32);
+}
+
+static void test_poll_server(int fd) {
+        struct pollfd pfd = { .fd = fd, .events = POLLIN };
+        int r;
+
+        r = poll(&pfd, 1, -1);
+        c_assert(r == 1);
+        c_assert(pfd.revents == POLLIN);
+}
+
+static void test_s_connection_init(int netns, NDhcp4SConnection *connection, int ifindex) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(netns);
+
+        r = n_dhcp4_s_connection_init(connection, ifindex);
+        c_assert(!r);
+
+        netns_set(oldns);
+}
+
+static void test_c_connection_listen(int netns, NDhcp4CConnection *connection) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(netns);
+
+        r = n_dhcp4_c_connection_listen(connection);
+        c_assert(!r);
+
+        netns_set(oldns);
+}
+
+static void test_c_connection_connect(int netns,
+                                      NDhcp4CConnection *connection,
+                                      const struct in_addr *client,
+                                      const struct in_addr *server) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(netns);
+
+        r = n_dhcp4_c_connection_connect(connection, client, server);
+        c_assert(!r);
+
+        netns_set(oldns);
+}
+
+static void test_server_receive(NDhcp4SConnection *connection, uint8_t expected_type, NDhcp4Incoming **messagep) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
+        uint8_t received_type;
+        int r, fd;
+
+        n_dhcp4_s_connection_get_fd(connection, &fd);
+        test_poll_server(fd);
+
+        r = n_dhcp4_s_connection_dispatch_io(connection, &message);
+        c_assert(!r);
+        c_assert(message);
+
+        r = n_dhcp4_incoming_query_message_type(message, &received_type);
+        c_assert(!r);
+        c_assert(received_type == expected_type);
+
+        if (messagep) {
+                *messagep = message;
+                message = NULL;
+        }
+}
+
+static void test_client_receive(NDhcp4CConnection *connection, uint8_t expected_type, NDhcp4Incoming **messagep) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
+        uint8_t received_type;
+        int r;
+
+        test_poll_client(connection->fd_epoll, N_DHCP4_CLIENT_EPOLL_IO);
+
+        r = n_dhcp4_c_connection_dispatch_io(connection, &message);
+        c_assert(!r);
+        c_assert(message);
+
+        r = n_dhcp4_incoming_query_message_type(message, &received_type);
+        c_assert(!r);
+        c_assert(received_type == expected_type);
+
+        if (messagep) {
+                *messagep = message;
+                message = NULL;
+        }
+}
+
+static void test_discover(NDhcp4SConnection *connection_server,
+                          NDhcp4CConnection *connection_client,
+                          const struct in_addr *addr_server,
+                          const struct in_addr *addr_client,
+                          NDhcp4Incoming **offerp) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply_out = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_discover_new(connection_client, &request_out);
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_DISCOVER, &request_in);
+
+        r = n_dhcp4_s_connection_offer_new(connection_server, &reply_out, request_in, addr_server, addr_client, 60);
+        c_assert(!r);
+
+        r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply_out);
+        c_assert(!r);
+
+        test_client_receive(connection_client, N_DHCP4_MESSAGE_OFFER, offerp);
+}
+
+static void test_select(NDhcp4SConnection *connection_server,
+                        NDhcp4CConnection *connection_client,
+                        NDhcp4Incoming *offer,
+                        const struct in_addr *addr_server,
+                        const struct in_addr *addr_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_select_new(connection_client, &request_out, offer);
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
+
+        r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
+        c_assert(!r);
+
+        r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
+        c_assert(!r);
+
+        test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, NULL);
+}
+
+static void test_reboot(NDhcp4SConnection *connection_server,
+                        NDhcp4CConnection *connection_client,
+                        const struct in_addr *addr_server,
+                        const struct in_addr *addr_client,
+                        NDhcp4Incoming **ackp) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_reboot_new(connection_client, &request_out, addr_server);
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
+
+        r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
+        c_assert(!r);
+
+        r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
+        c_assert(!r);
+
+        test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, ackp);
+}
+
+static void test_decline(NDhcp4SConnection *connection_server,
+                         NDhcp4CConnection *connection_client,
+                         NDhcp4Incoming *ack) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_decline_new(connection_client, &request_out, ack, "No thanks.");
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_DECLINE, NULL);
+}
+
+
+static void test_renew(NDhcp4SConnection *connection_server,
+                       NDhcp4CConnection *connection_client,
+                       const struct in_addr *addr_server,
+                       const struct in_addr *addr_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_renew_new(connection_client, &request_out);
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
+
+        r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
+        c_assert(!r);
+
+        r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
+        c_assert(!r);
+
+        test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, NULL);
+}
+
+static void test_rebind(NDhcp4SConnection *connection_server,
+                        NDhcp4CConnection *connection_client,
+                        const struct in_addr *addr_server,
+                        const struct in_addr *addr_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_rebind_new(connection_client, &request_out);
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
+
+        r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
+        c_assert(!r);
+
+        r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
+        c_assert(!r);
+
+        test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, NULL);
+}
+
+static void test_release(NDhcp4SConnection *connection_server,
+                         NDhcp4CConnection *connection_client,
+                         const struct in_addr *addr_server,
+                         const struct in_addr *addr_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
+        int r;
+
+        r = n_dhcp4_c_connection_release_new(connection_client, &request_out, "Shutting down!");
+        c_assert(!r);
+
+        r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
+        c_assert(!r);
+        request_out = NULL;
+
+        test_server_receive(connection_server, N_DHCP4_MESSAGE_RELEASE, NULL);
+}
+
+static void test_connection(void) {
+        const struct in_addr addr_server = (struct in_addr){ htonl(10 << 24 | 1) };
+        const struct in_addr addr_client = (struct in_addr){ htonl(10 << 24 | 2) };
+        _c_cleanup_(netns_closep) int ns_server = -1, ns_client = -1;
+        _c_cleanup_(link_deinit) Link link_server = LINK_NULL(link_server);
+        _c_cleanup_(link_deinit) Link link_client = LINK_NULL(link_client);
+        _c_cleanup_(c_closep) int efd_client = -1;
+        int r;
+
+        /* setup */
+
+        netns_new(&ns_server);
+        netns_new(&ns_client);
+
+        link_new_veth(&link_server, &link_client, ns_server, ns_client);
+        link_add_ip4(&link_server, &addr_server, 8);
+
+        efd_client = epoll_create1(EPOLL_CLOEXEC);
+        c_assert(efd_client >= 0);
+
+        /* test connections */
+        {
+                _c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *client_config = NULL;
+                _c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *probe_config = NULL;
+                NDhcp4SConnection connection_server = N_DHCP4_S_CONNECTION_NULL(connection_server);
+                NDhcp4SConnectionIp connection_server_ip = N_DHCP4_S_CONNECTION_IP_NULL(connection_server_ip);
+                NDhcp4CConnection connection_client = N_DHCP4_C_CONNECTION_NULL(connection_client);
+                _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *offer = NULL;
+                _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *ack = NULL;
+                NDhcp4LogQueue log_queue = N_DHCP4_LOG_QUEUE_NULL_DEFUNCT();
+
+                test_s_connection_init(ns_server, &connection_server, link_server.ifindex);
+                n_dhcp4_s_connection_ip_init(&connection_server_ip, addr_server);
+                n_dhcp4_s_connection_ip_link(&connection_server_ip, &connection_server);
+
+                r = n_dhcp4_client_config_new(&client_config);
+                c_assert(!r);
+
+                n_dhcp4_client_config_set_ifindex(client_config, link_client.ifindex);
+                n_dhcp4_client_config_set_transport(client_config, N_DHCP4_TRANSPORT_ETHERNET);
+                n_dhcp4_client_config_set_request_broadcast(client_config, false);
+                n_dhcp4_client_config_set_mac(client_config, link_client.mac.ether_addr_octet, ETH_ALEN);
+                n_dhcp4_client_config_set_broadcast_mac(client_config,
+                                                        (const uint8_t[]){
+                                                                0xff, 0xff, 0xff,
+                                                                0xff, 0xff, 0xff,
+                                                        },
+                                                        ETH_ALEN);
+                r = n_dhcp4_client_config_set_client_id(client_config,
+                                                        (void *)"client-id",
+                                                        strlen("client-id"));
+                c_assert(!r);
+
+                r = n_dhcp4_client_probe_config_new(&probe_config);
+                c_assert(!r);
+
+                r = n_dhcp4_c_connection_init(&connection_client,
+                                              client_config,
+                                              probe_config,
+                                              &log_queue,
+                                              efd_client);
+                c_assert(!r);
+                test_c_connection_listen(ns_client, &connection_client);
+
+                test_discover(&connection_server, &connection_client, &addr_server, &addr_client, &offer);
+                test_select(&connection_server, &connection_client, offer, &addr_server, &addr_client);
+                test_reboot(&connection_server, &connection_client, &addr_server, &addr_client, &ack);
+                test_rebind(&connection_server, &connection_client, &addr_server, &addr_client);
+                test_decline(&connection_server, &connection_client, ack);
+
+                link_add_ip4(&link_client, &addr_client, 8);
+                test_c_connection_connect(ns_client, &connection_client, &addr_client, &addr_server);
+
+                test_renew(&connection_server, &connection_client, &addr_server, &addr_client);
+                test_release(&connection_server, &connection_client, &addr_server, &addr_client);
+
+                n_dhcp4_c_connection_deinit(&connection_client);
+                n_dhcp4_s_connection_ip_unlink(&connection_server_ip);
+                n_dhcp4_s_connection_ip_deinit(&connection_server_ip);
+                n_dhcp4_s_connection_deinit(&connection_server);
+        }
+
+        /* teardown */
+
+        link_del_ip4(&link_client, &addr_client, 8);
+        link_del_ip4(&link_server, &addr_server, 8);
+}
+
+int main(int argc, char **argv) {
+        test_setup();
+
+        test_connection();
+
+        return 0;
+}
diff --git a/src/n-dhcp4/src/test-message.c b/src/n-dhcp4/src/test-message.c
new file mode 100644
index 00000000..6d4200f7
--- /dev/null
+++ b/src/n-dhcp4/src/test-message.c
@@ -0,0 +1,159 @@
+/*
+ * Tests for DHCP4 Message Handling
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <endian.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "n-dhcp4-private.h"
+
+static void test_outgoing(void) {
+        NDhcp4Outgoing *outgoing;
+        int r;
+
+        /* verify basic NEW/FREE */
+
+        outgoing = NULL;
+        r = n_dhcp4_outgoing_new(&outgoing, 0, 0);
+        c_assert(!r);
+        c_assert(outgoing);
+
+        outgoing = n_dhcp4_outgoing_free(outgoing);
+        c_assert(!outgoing);
+}
+
+static void test_incoming(void) {
+        NDhcp4Incoming *incoming;
+        struct {
+                NDhcp4Header header;
+                uint8_t sname[64];
+                uint8_t file[128];
+                uint32_t magic;
+                uint8_t options[1024];
+        } m;
+        uint8_t *v;
+        size_t l;
+        int r;
+
+        /* verify that messages must be at least the size of the header */
+
+        r = n_dhcp4_incoming_new(&incoming, NULL, 0);
+        c_assert(r == N_DHCP4_E_MALFORMED);
+
+        r = n_dhcp4_incoming_new(&incoming, NULL, sizeof(m.header) + 64 + 128 + 3);
+        c_assert(r == N_DHCP4_E_MALFORMED);
+
+        /* verify that magic must be set */
+
+        memset(&m, 0, sizeof(m));
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(r == N_DHCP4_E_MALFORMED);
+
+        /* verify basic NEW/FREE */
+
+        memset(&m, 0, sizeof(m));
+        m.magic = htobe32(N_DHCP4_MESSAGE_MAGIC);
+        incoming = NULL;
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m.header) + 64 + 128 + 4);
+        c_assert(!r);
+        c_assert(incoming);
+
+        incoming = n_dhcp4_incoming_free(incoming);
+        c_assert(!incoming);
+
+        /* verify that PAD is properly handled */
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        incoming = n_dhcp4_incoming_free(incoming);
+
+        /* verify that SNAME/FILE are only looked at if OVERLOAD is set */
+
+        m.sname[0] = 1;
+        m.sname[1] = 0;
+        m.file[0] = 2;
+        m.file[1] = 0;
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        r = n_dhcp4_incoming_query(incoming, 1, NULL, NULL);
+        c_assert(r == N_DHCP4_E_UNSET);
+        r = n_dhcp4_incoming_query(incoming, 2, NULL, NULL);
+        c_assert(r == N_DHCP4_E_UNSET);
+        incoming = n_dhcp4_incoming_free(incoming);
+
+        m.options[0] = N_DHCP4_OPTION_OVERLOAD;
+        m.options[1] = 1;
+        m.options[2] = 0;
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        r = n_dhcp4_incoming_query(incoming, 1, NULL, NULL);
+        c_assert(r == N_DHCP4_E_UNSET);
+        r = n_dhcp4_incoming_query(incoming, 2, NULL, NULL);
+        c_assert(r == N_DHCP4_E_UNSET);
+        incoming = n_dhcp4_incoming_free(incoming);
+
+        m.options[0] = N_DHCP4_OPTION_OVERLOAD;
+        m.options[1] = 1;
+        m.options[2] = N_DHCP4_OVERLOAD_SNAME;
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        r = n_dhcp4_incoming_query(incoming, 1, NULL, NULL);
+        c_assert(r == 0);
+        r = n_dhcp4_incoming_query(incoming, 2, NULL, NULL);
+        c_assert(r == N_DHCP4_E_UNSET);
+        incoming = n_dhcp4_incoming_free(incoming);
+
+        m.options[0] = N_DHCP4_OPTION_OVERLOAD;
+        m.options[1] = 1;
+        m.options[2] = N_DHCP4_OVERLOAD_FILE;
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        r = n_dhcp4_incoming_query(incoming, 1, NULL, NULL);
+        c_assert(r == N_DHCP4_E_UNSET);
+        r = n_dhcp4_incoming_query(incoming, 2, NULL, NULL);
+        c_assert(r == 0);
+        incoming = n_dhcp4_incoming_free(incoming);
+
+        m.options[0] = N_DHCP4_OPTION_OVERLOAD;
+        m.options[1] = 1;
+        m.options[2] = N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME;
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        r = n_dhcp4_incoming_query(incoming, 1, NULL, NULL);
+        c_assert(r == 0);
+        r = n_dhcp4_incoming_query(incoming, 2, NULL, NULL);
+        c_assert(r == 0);
+        incoming = n_dhcp4_incoming_free(incoming);
+
+        /* verify basic concatenation */
+
+        m.options[3] = 1;
+        m.options[4] = 1;
+        m.options[5] = 0xef;
+        m.sname[1] = 1;
+        m.sname[2] = 0xcf;
+
+        r = n_dhcp4_incoming_new(&incoming, &m, sizeof(m));
+        c_assert(!r);
+        r = n_dhcp4_incoming_query(incoming, 1, &v, &l);
+        c_assert(r == 0);
+        c_assert(l == 2);
+        c_assert(v[0] == 0xef && v[1] == 0xcf);
+        incoming = n_dhcp4_incoming_free(incoming);
+}
+
+int main(int argc, char **argv) {
+        test_outgoing();
+        test_incoming();
+        return 0;
+}
diff --git a/src/n-dhcp4/src/test-run-client.c b/src/n-dhcp4/src/test-run-client.c
new file mode 100644
index 00000000..259a9608
--- /dev/null
+++ b/src/n-dhcp4/src/test-run-client.c
@@ -0,0 +1,713 @@
+/*
+ * DHCP Client Runner
+ *
+ * This test implements a DHCP client. It takes parameters via the command-line
+ * and runs a DHCP client. It is mainly meant for testing, as such it allows
+ * tweaking that an exported DHCP client should not provide.
+ */
+
+#include <assert.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "n-dhcp4.h"
+#include "n-dhcp4-private.h"
+#include "test.h"
+
+typedef struct Manager Manager;
+
+enum {
+        _MAIN_SUCCESS,
+        MAIN_EXIT,
+        MAIN_FAILED,
+};
+
+struct Manager {
+        NDhcp4Client *client;
+        NDhcp4ClientProbe *probe;
+};
+
+#define MANAGER_NULL(_x) {}
+
+static struct ether_addr        main_arg_broadcast_mac = {};
+static bool                     main_arg_broadcast_mac_set = false;
+static uint8_t*                 main_arg_client_id = NULL;
+static size_t                   main_arg_n_client_id = 0;
+static int                      main_arg_ifindex = 0;
+static struct in_addr           main_arg_requested_ip = { INADDR_ANY };
+static long long int            main_arg_requested_lifetime = -1;
+static uint8_t                  main_arg_requested_parameters[UINT8_MAX] = {};
+static size_t                   main_arg_n_requested_parameters = 0;
+static struct ether_addr        main_arg_mac = {};
+static bool                     main_arg_mac_set = false;
+static bool                     main_arg_request_broadcast = false;
+static bool                     main_arg_test = false;
+
+static Manager *manager_free(Manager *manager) {
+        if (!manager)
+                return NULL;
+
+        n_dhcp4_client_probe_free(manager->probe);
+        n_dhcp4_client_unref(manager->client);
+        free(manager);
+
+        return NULL;
+}
+
+static void manager_freep(Manager **manager) {
+        manager_free(*manager);
+}
+
+static int manager_new(Manager **managerp) {
+        _c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *config = NULL;
+        _c_cleanup_(manager_freep) Manager *manager = NULL;
+        int r;
+
+        manager = malloc(sizeof(*manager));
+        if (!manager)
+                return -ENOMEM;
+
+        *manager = (Manager)MANAGER_NULL(*manager);
+
+        r = n_dhcp4_client_config_new(&config);
+        if (r)
+                return r;
+
+        n_dhcp4_client_config_set_broadcast_mac(config,
+                                                &main_arg_broadcast_mac.ether_addr_octet[0],
+                                                sizeof(main_arg_broadcast_mac.ether_addr_octet));
+        n_dhcp4_client_config_set_mac(config,
+                                      &main_arg_mac.ether_addr_octet[0],
+                                      sizeof(main_arg_mac.ether_addr_octet));
+        n_dhcp4_client_config_set_client_id(config,
+                                            main_arg_client_id,
+                                            main_arg_n_client_id);
+        n_dhcp4_client_config_set_ifindex(config, main_arg_ifindex);
+        n_dhcp4_client_config_set_request_broadcast(config, main_arg_request_broadcast);
+        n_dhcp4_client_config_set_transport(config, N_DHCP4_TRANSPORT_ETHERNET);
+
+        r = n_dhcp4_client_new(&manager->client, config);
+        if (r)
+                return r;
+
+        *managerp = manager;
+        manager = NULL;
+        return 0;
+}
+
+static int manager_lease_get_dns(NDhcp4ClientLease *lease, struct in_addr *dns) {
+        uint8_t *data;
+        size_t n_data;
+        int r;
+
+        r = n_dhcp4_client_lease_query(lease, N_DHCP4_OPTION_DOMAIN_NAME_SERVER, &data, &n_data);
+        if (r)
+                return r;
+
+        if (n_data < sizeof(dns->s_addr))
+                return N_DHCP4_E_MALFORMED;
+
+        memcpy(&dns->s_addr, data, sizeof(dns->s_addr));
+
+        return 0;
+}
+
+static int manager_lease_get_router(NDhcp4ClientLease *lease, struct in_addr *router) {
+        uint8_t *data;
+        size_t n_data;
+        int r;
+
+        r = n_dhcp4_client_lease_query(lease, N_DHCP4_OPTION_ROUTER, &data, &n_data);
+        if (r)
+                return r;
+
+        if (n_data < sizeof(router->s_addr))
+                return N_DHCP4_E_MALFORMED;
+
+        memcpy(&router->s_addr, data, sizeof(router->s_addr));
+
+        return 0;
+}
+
+static int manager_lease_get_subnetmask(NDhcp4ClientLease *lease, struct in_addr *mask) {
+        uint8_t *data;
+        size_t n_data;
+        int r;
+
+        r = n_dhcp4_client_lease_query(lease, N_DHCP4_OPTION_SUBNET_MASK, &data, &n_data);
+        if (r)
+                return r;
+
+        if (n_data != sizeof(mask->s_addr))
+                return N_DHCP4_E_MALFORMED;
+
+        memcpy(&mask->s_addr, data, sizeof(mask->s_addr));
+
+        return 0;
+}
+
+static int manager_lease_get_prefix(NDhcp4ClientLease *lease, unsigned int *prefixp) {
+        struct in_addr mask = {};
+        unsigned int postfix;
+        int r;
+
+        r = manager_lease_get_subnetmask(lease, &mask);
+        if (r)
+                return r;
+
+        postfix =__builtin_ctz(ntohl(mask.s_addr));
+        c_assert(postfix <= 32);
+
+        if (postfix < 32) {
+                if ((~ntohl(mask.s_addr)) >> postfix != 0)
+                        return N_DHCP4_E_MALFORMED;
+        }
+
+        *prefixp = 32 - postfix;
+        return 0;
+}
+
+static int manager_check(Manager *manager, NDhcp4ClientLease *lease) {
+        int r;
+
+        r = n_dhcp4_client_lease_query(lease, N_DHCP4_OPTION_ROUTER, NULL, NULL);
+        if (r) {
+                fprintf(stderr, "No router\n");
+                return r;
+        }
+
+        r = n_dhcp4_client_lease_query(lease, N_DHCP4_OPTION_SUBNET_MASK, NULL, NULL);
+        if (r) {
+                fprintf(stderr, "No subnet mask\n");
+                return r;
+        }
+
+        return r;
+}
+
+static int manager_add(Manager *manager, NDhcp4ClientLease *lease) {
+        char *p, ifname[IF_NAMESIZE + 1] = {};
+        struct in_addr router = {}, yiaddr = {}, dns = {};
+        unsigned int prefix;
+        uint64_t lifetime;
+        int r;
+
+        n_dhcp4_client_lease_get_yiaddr(lease, &yiaddr);
+        n_dhcp4_client_lease_get_lifetime(lease, &lifetime);
+
+        r = manager_lease_get_router(lease, &router);
+        if (r)
+                return r;
+
+        r = manager_lease_get_prefix(lease, &prefix);
+        if (r)
+                return r;
+
+        p = if_indextoname(main_arg_ifindex, ifname);
+        c_assert(p);
+
+        if (lifetime == UINT64_MAX) {
+                r = asprintf(&p, "ip addr add %s/%u dev %s preferred_lft forever valid_lft forever", inet_ntoa(yiaddr), prefix, ifname);
+                c_assert(r >= 0);
+        } else {
+                r = asprintf(&p, "ip addr add %s/%u dev %s preferred_lft %llu valid_lft %llu", inet_ntoa(yiaddr), prefix, ifname, lifetime / 1000000000ULL, lifetime / 1000000000ULL);
+                c_assert(r >= 0);
+        }
+        r = system(p);
+        c_assert(r == 0);
+        free(p);
+
+        r = asprintf(&p, "ip route add %s/32 dev %s", inet_ntoa(router), ifname);
+        c_assert(r >= 0);
+        r = system(p);
+        c_assert(r == 0);
+        free(p);
+
+        r = asprintf(&p, "ip route add default via %s dev %s", inet_ntoa(router), ifname);
+        c_assert(r >= 0);
+        r = system(p);
+        c_assert(r == 0);
+        free(p);
+
+        r = manager_lease_get_dns(lease, &dns);
+        if (r) {
+                if (r != N_DHCP4_E_UNSET)
+                        return r;
+        } else {
+                fprintf(stderr, "DNS: %s\n", inet_ntoa(dns));
+        }
+
+        return 0;
+}
+
+static int manager_dispatch(Manager *manager) {
+        NDhcp4ClientEvent *event;
+        int r;
+
+        r = n_dhcp4_client_dispatch(manager->client);
+        if (r) {
+                if (r != N_DHCP4_E_PREEMPTED) {
+                        /*
+                         * We are level-triggered, so we do not need to react
+                         * to preemption. We simply continue the mainloop.
+                         */
+                        return r;
+                }
+        }
+
+        for (;;) {
+                r = n_dhcp4_client_pop_event(manager->client, &event);
+                if (r)
+                        return r;
+
+                if (!event)
+                        break;
+
+                switch (event->event) {
+                case N_DHCP4_CLIENT_EVENT_DOWN:
+                        fprintf(stderr, "DOWN\n");
+
+                        break;
+
+                case N_DHCP4_CLIENT_EVENT_OFFER:
+                        fprintf(stderr, "OFFER\n");
+
+                        r = manager_check(manager, event->granted.lease);
+                        if (r) {
+                                if (r == N_DHCP4_E_UNSET) {
+                                        fprintf(stderr, "Missing mandatory option, ignoring lease.\n");
+                                } else {
+                                        return r;
+                                }
+                        } else {
+                                r = n_dhcp4_client_lease_select(event->offer.lease);
+                                if (r)
+                                        return r;
+                        }
+
+                        break;
+
+                case N_DHCP4_CLIENT_EVENT_GRANTED:
+                        fprintf(stderr, "GRANTED\n");
+
+                        r = manager_add(manager, event->granted.lease);
+                        if (r) {
+                                if (r == N_DHCP4_E_UNSET) {
+                                        fprintf(stderr, "Missing mandatory option, declining lease.\n");
+
+                                        r = n_dhcp4_client_lease_decline(event->granted.lease, "Missing mandatory option.");
+                                        if (r)
+                                                return r;
+                                } else {
+                                        return r;
+                                }
+                        } else {
+                                r = n_dhcp4_client_lease_accept(event->granted.lease);
+                                if (r)
+                                        return r;
+                        }
+
+                        break;
+
+                case N_DHCP4_CLIENT_EVENT_RETRACTED:
+                        fprintf(stderr, "RETRACTED\n");
+
+                        break;
+
+                case N_DHCP4_CLIENT_EVENT_EXTENDED:
+                        fprintf(stderr, "EXTENDED\n");
+
+                        break;
+
+                case N_DHCP4_CLIENT_EVENT_EXPIRED:
+                        fprintf(stderr, "EXPIRED\n");
+
+                        break;
+
+                case N_DHCP4_CLIENT_EVENT_CANCELLED:
+                        fprintf(stderr, "CANCELLED\n");
+
+                        break;
+
+                default:
+                        fprintf(stderr, "Unexpected event: %u\n", event->event);
+
+                        break;
+                }
+        }
+
+        return 0;
+}
+
+static int manager_run(Manager *manager) {
+        _c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL;
+        int r;
+
+        r = n_dhcp4_client_probe_config_new(&config);
+        if (r)
+                return r;
+
+        /*
+         * Let's speed up our tests, while still making sure the code-path
+         * for the deferrment is actually tested (so don't set it to zero).
+         */
+        n_dhcp4_client_probe_config_set_start_delay(config, 10);
+
+        n_dhcp4_client_probe_config_set_requested_ip(config, main_arg_requested_ip);
+
+        if (main_arg_n_requested_parameters > 0) {
+                for (unsigned int i = 0; i < main_arg_n_requested_parameters; ++i)
+                        n_dhcp4_client_probe_config_request_option(config, main_arg_requested_parameters[i]);
+        } else {
+                n_dhcp4_client_probe_config_request_option(config, N_DHCP4_OPTION_ROUTER);
+                n_dhcp4_client_probe_config_request_option(config, N_DHCP4_OPTION_SUBNET_MASK);
+                n_dhcp4_client_probe_config_request_option(config, N_DHCP4_OPTION_DOMAIN_NAME_SERVER);
+        }
+
+        if (main_arg_requested_lifetime >= 0) {
+                uint32_t lifetime = ntohl(main_arg_requested_lifetime);
+
+                r = n_dhcp4_client_probe_config_append_option(config, N_DHCP4_OPTION_IP_ADDRESS_LEASE_TIME, &lifetime, sizeof(lifetime));
+                if (r)
+                        return r;
+        }
+
+        r = n_dhcp4_client_probe(manager->client, &manager->probe, config);
+        if (r)
+                return r;
+
+        /*
+         * The test-suite runs this with the --test argument. So far, we do not
+         * perform any fancy runtime tests, but simply exit the main-loop
+         * immediately. We can add more elaborate tests in the future.
+         */
+        if (main_arg_test)
+                return 0;
+
+        for (;;) {
+                struct pollfd pfds[] = {
+                        { .fd = -1, .events = POLLIN },
+                };
+                size_t i;
+                int n;
+
+                n_dhcp4_client_get_fd(manager->client, &pfds[0].fd);
+
+                n = poll(pfds, sizeof(pfds) / sizeof(*pfds), -1);
+                if (n < 0)
+                        return -errno;
+
+                for (i = 0; i < (size_t)n; ++i) {
+                        if (pfds[i].revents & ~POLLIN)
+                                return -ENOTRECOVERABLE;
+
+                        if (!(pfds[i].revents & POLLIN))
+                                continue;
+
+                        r = manager_dispatch(manager);
+                        if (r)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+static int run(void) {
+        _c_cleanup_(manager_freep) Manager *manager = NULL;
+        int r;
+
+        r = manager_new(&manager);
+        if (r)
+                return r;
+
+        return manager_run(manager);
+}
+
+static void print_help(void) {
+        printf("%s [GLOBALS...] ...\n\n"
+               "DHCP Test Client\n\n"
+               "  -h --help                            Show this help\n"
+               "     --test                            Run as part of the test suite\n"
+               "     --ifindex IDX                     Index of interface to run on\n"
+               "     --mac HEX                         Hardware address to use\n"
+               "     --broadcast-mac HEX               Broadcast hardware address to use\n"
+               "     --requested-ip IP                 Requested IP address\n"
+               "     --requested-lifetime SECS         Requested lease lifetime in seconds\n"
+               "     --requested-parameters P1,P2,...  Requested parameters\n"
+               "     --client-id HEX                   Client Identifier to use\n"
+               , program_invocation_short_name);
+}
+
+static int setup_test(void) {
+        test_setup();
+
+        /* --broadcast-mac */
+        {
+                main_arg_broadcast_mac_set = true;
+        }
+
+        /* --ifindex */
+        {
+                main_arg_ifindex = 1;
+        }
+
+        /* --mac */
+        {
+                main_arg_mac_set = true;
+        }
+
+        return 0;
+}
+
+static int parse_hexstr(const char *in, uint8_t **outp, size_t *n_outp) {
+        _c_cleanup_(c_freep) uint8_t *out = NULL;
+        size_t i, n_in, n_out;
+
+        n_in = strlen(in);
+        n_out = (n_in + 1) / 2;
+
+        out = malloc(n_out);
+        if (!out)
+                return -ENOMEM;
+
+        for (i = 0; i < n_in; ++i) {
+                uint8_t v = 0;
+
+                switch (in[i]) {
+                case '0'...'9':
+                        v = in[i] - '0';
+                        break;
+                case 'a'...'f':
+                        v = in[i] - 'a' + 0xa;
+                        break;
+                case 'A'...'F':
+                        v = in[i] - 'A' + 0xa;
+                        break;
+                }
+
+                if (i % 2) {
+                        out[i / 2] <<= 4;
+                        out[i / 2] |= v;
+                } else {
+                        out[i / 2] = v;
+                }
+        }
+
+        *outp = out;
+        out = NULL;
+        *n_outp = n_out;
+        return 0;
+}
+
+static int parse_argv(int argc, char **argv) {
+        enum {
+                _ARG_0 = 0x100,
+                ARG_BROADCAST_MAC,
+                ARG_CLIENT_ID,
+                ARG_IFINDEX,
+                ARG_MAC,
+                ARG_REQUEST_BROADCAST,
+                ARG_REQUESTED_IP,
+                ARG_REQUESTED_LIFETIME,
+                ARG_REQUESTED_PARAMETERS,
+                ARG_TEST,
+        };
+        static const struct option options[] = {
+                { "help",                       no_argument,            NULL,   'h'                             },
+                { "broadcast-mac",              required_argument,      NULL,   ARG_BROADCAST_MAC               },
+                { "client-id",                  required_argument,      NULL,   ARG_CLIENT_ID                   },
+                { "ifindex",                    required_argument,      NULL,   ARG_IFINDEX                     },
+                { "mac",                        required_argument,      NULL,   ARG_MAC                         },
+                { "request-broadcast",          no_argument,            NULL,   ARG_REQUEST_BROADCAST           },
+                { "requested-ip",               required_argument,      NULL,   ARG_REQUESTED_IP                },
+                { "requested-lifetime",         required_argument,      NULL,   ARG_REQUESTED_LIFETIME          },
+                { "requested-parameters",       required_argument,      NULL,   ARG_REQUESTED_PARAMETERS        },
+                { "test",                       no_argument,            NULL,   ARG_TEST                        },
+                {}
+        };
+        struct ether_addr *addr;
+        long long int lli;
+        size_t n;
+        void *t;
+        int r, c;
+
+        /*
+         * Most of the argument-parsers are short-and-dirty hacks to make the
+         * conversions work. This is sufficient for a test-client, but needs
+         * proper error-checking if done outside of tests.
+         */
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+                switch (c) {
+                case 'h':
+                        print_help();
+                        return MAIN_EXIT;
+
+                case ARG_BROADCAST_MAC:
+                        addr = ether_aton_r(optarg, &main_arg_broadcast_mac);
+                        if (!addr) {
+                                fprintf(stderr,
+                                        "%s: invalid broadcast mac address -- '%s'\n",
+                                        program_invocation_name,
+                                        optarg);
+                                return MAIN_FAILED;
+                        }
+
+                        main_arg_broadcast_mac_set = true;
+                        break;
+
+                case ARG_CLIENT_ID:
+                        r = parse_hexstr(optarg, (uint8_t **)&t, &n);
+                        if (r)
+                                return r;
+
+                        free(main_arg_client_id);
+                        main_arg_client_id = t;
+                        main_arg_n_client_id = n;
+                        break;
+
+                case ARG_IFINDEX:
+                        main_arg_ifindex = atoi(optarg);
+                        break;
+
+                case ARG_MAC:
+                        addr = ether_aton_r(optarg, &main_arg_mac);
+                        if (!addr) {
+                                fprintf(stderr,
+                                        "%s: invalid mac address -- '%s'\n",
+                                        program_invocation_name,
+                                        optarg);
+                                return MAIN_FAILED;
+                        }
+
+                        main_arg_mac_set = true;
+                        break;
+
+                case ARG_REQUEST_BROADCAST:
+                        main_arg_request_broadcast = true;
+                        break;
+
+                case ARG_REQUESTED_IP:
+                        r = inet_pton(AF_INET, optarg, &main_arg_requested_ip);
+                        if (r != 1) {
+                                fprintf(stderr,
+                                        "%s: invalid requested IP -- '%s'\n",
+                                        program_invocation_name,
+                                        optarg);
+                                return MAIN_FAILED;
+                        }
+                        break;
+
+                case ARG_REQUESTED_LIFETIME:
+                        lli = atoll(optarg);
+                        if (lli < 0 || lli > UINT32_MAX) {
+                                fprintf(stderr,
+                                        "%s: invalid requested lifetime -- '%s'\n",
+                                        program_invocation_name,
+                                        optarg);
+                                return MAIN_FAILED;
+                        }
+                        main_arg_requested_lifetime = lli;
+                        break;
+
+                case ARG_REQUESTED_PARAMETERS:
+                        for (const char *param = optarg; param; param = strchr(param, ',') ? strchr(param, ',')  + 1 : NULL) {
+                                c_assert(main_arg_n_requested_parameters <= UINT8_MAX);
+
+                                lli = atoll(param);
+                                if (lli < 0 || lli > UINT8_MAX) {
+                                        fprintf(stderr,
+                                                "%s: invalid requested parameters -- '%s'\n",
+                                                program_invocation_name,
+                                                optarg);
+                                        return MAIN_FAILED;
+                                }
+                                main_arg_requested_parameters[main_arg_n_requested_parameters++] = lli;
+                        }
+                        break;
+
+                case ARG_TEST:
+                        r = setup_test();
+                        if (r)
+                                return r;
+
+                        main_arg_test = true;
+                        break;
+
+                case '?':
+                        /* getopt_long() prints warning */
+                        return MAIN_FAILED;
+
+                default:
+                        return -ENOTRECOVERABLE;
+                }
+        }
+
+        if (optind != argc) {
+                fprintf(stderr,
+                        "%s: invalid arguments -- '%s'\n",
+                        program_invocation_name,
+                        argv[optind]);
+                return MAIN_FAILED;
+        }
+
+        if (!main_arg_broadcast_mac_set ||
+            !main_arg_ifindex ||
+            !main_arg_mac_set) {
+                fprintf(stderr,
+                        "%s: required arguments: broadcast-mac, ifindex, mac\n",
+                        program_invocation_name);
+                return MAIN_FAILED;
+        }
+
+        return 0;
+}
+
+int main(int argc, char **argv) {
+        int r;
+
+        /* --client-id */
+        {
+                uint8_t *b;
+                size_t n;
+
+                n = strlen("client-id");
+                b = malloc(n);
+                c_assert(b);
+                memcpy(b, "client-id", n);
+
+                free(main_arg_client_id);
+                main_arg_client_id = b;
+                main_arg_n_client_id = n;
+        }
+
+        r = parse_argv(argc, argv);
+        if (r)
+                goto exit;
+
+        r = run();
+
+exit:
+        if (r == MAIN_EXIT) {
+                r = 0;
+        } else if (r < 0) {
+                errno = -r;
+                fprintf(stderr, "Failed with system errno %d: %m\n", r);
+                r = 127;
+        } else if (r > 0) {
+                fprintf(stderr, "Failed with internal error %d\n", r);
+        }
+
+        free(main_arg_client_id);
+
+        return r;
+}
diff --git a/src/n-dhcp4/src/test-socket.c b/src/n-dhcp4/src/test-socket.c
new file mode 100644
index 00000000..2b9303af
--- /dev/null
+++ b/src/n-dhcp4/src/test-socket.c
@@ -0,0 +1,317 @@
+/*
+ * Tests for DHCP4 Socket Helpers
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <endian.h>
+#include <errno.h>
+#include <poll.h>
+#include <linux/if_packet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "n-dhcp4-private.h"
+#include "test.h"
+#include "util/link.h"
+#include "util/netns.h"
+#include "util/packet.h"
+
+static void test_poll(int sk) {
+        int r;
+
+        r = poll(&(struct pollfd){ .fd = sk, .events = POLLIN }, 1, -1);
+        c_assert(r == 1);
+}
+
+static void test_client_packet_socket_new(Link *link, int *skp) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(link->netns);
+
+        r = n_dhcp4_c_socket_packet_new(skp, link->ifindex);
+        c_assert(r >= 0);
+
+        netns_set(oldns);
+}
+
+static void test_client_udp_socket_new(Link *link,
+                                       int *skp,
+                                       const struct in_addr *addr_client,
+                                       const struct in_addr *addr_server) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(link->netns);
+
+        r = n_dhcp4_c_socket_udp_new(skp, link->ifindex, addr_client, addr_server, N_DHCP4_DSCP_DEFAULT);
+        c_assert(r >= 0);
+
+        netns_set(oldns);
+}
+
+static void test_server_packet_socket_new(Link *link, int *skp) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(link->netns);
+
+        r = n_dhcp4_s_socket_packet_new(skp);
+        c_assert(r >= 0);
+
+        netns_set(oldns);
+}
+
+static void test_server_udp_socket_new(Link *link, int *skp) {
+        int r, oldns;
+
+        netns_get(&oldns);
+        netns_set(link->netns);
+
+        r = n_dhcp4_s_socket_udp_new(skp, link->ifindex);
+        c_assert(r >= 0);
+
+        netns_set(oldns);
+}
+
+static void test_client_server_packet(Link *link_server, Link *link_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *incoming = NULL;
+        _c_cleanup_(c_closep) int sk_server = -1, sk_client = -1;
+        uint8_t buf[UINT16_MAX];
+        struct sockaddr_in dest = {};
+        int r;
+
+        test_server_udp_socket_new(link_server, &sk_server);
+        test_client_packet_socket_new(link_client, &sk_client);
+
+        r = n_dhcp4_outgoing_new(&outgoing, 0, 0);
+        c_assert(!r);
+        n_dhcp4_outgoing_get_header(outgoing)->op = N_DHCP4_OP_BOOTREQUEST;
+
+        r = n_dhcp4_c_socket_packet_send(sk_client,
+                                         link_client->ifindex,
+                                         (const unsigned char[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+                                         ETH_ALEN,
+                                         -1,
+                                         outgoing);
+        c_assert(!r);
+
+        test_poll(sk_server);
+
+        r = n_dhcp4_s_socket_udp_recv(sk_server, buf, sizeof(buf), &incoming, &dest);
+        c_assert(!r);
+        c_assert(incoming);
+        c_assert(dest.sin_family == AF_INET);
+        c_assert(dest.sin_port == htons(N_DHCP4_NETWORK_SERVER_PORT));
+        c_assert(dest.sin_addr.s_addr == INADDR_BROADCAST);
+}
+
+static void test_client_server_udp(Link *link_server, Link *link_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *incoming = NULL;
+        _c_cleanup_(c_closep) int sk_server = -1, sk_client = -1;
+        struct in_addr addr_server = (struct in_addr){ htonl(10 << 24 | 1) };
+        struct in_addr addr_client = (struct in_addr){ htonl(10 << 24 | 2) };
+        uint8_t buf[UINT16_MAX];
+        struct sockaddr_in dest = {};
+        int r;
+
+        /* setup */
+
+        link_add_ip4(link_server, &addr_server, 8);
+        link_add_ip4(link_client, &addr_client, 8);
+
+        /* test communication */
+
+        test_server_udp_socket_new(link_server, &sk_server);
+        test_client_udp_socket_new(link_client, &sk_client, &addr_client, &addr_server);
+
+        r = n_dhcp4_outgoing_new(&outgoing, 0, 0);
+        c_assert(!r);
+        n_dhcp4_outgoing_get_header(outgoing)->op = N_DHCP4_OP_BOOTREQUEST;
+
+        r = n_dhcp4_c_socket_udp_send(sk_client, outgoing);
+        c_assert(!r);
+
+        test_poll(sk_server);
+
+        r = n_dhcp4_s_socket_udp_recv(sk_server, buf, sizeof(buf), &incoming, &dest);
+        c_assert(!r);
+        c_assert(incoming);
+        c_assert(dest.sin_family == AF_INET);
+        c_assert(dest.sin_port == htons(N_DHCP4_NETWORK_SERVER_PORT));
+        c_assert(dest.sin_addr.s_addr == addr_server.s_addr);
+
+        /* teardown */
+
+        link_del_ip4(link_client, &addr_client, 8);
+        link_del_ip4(link_server, &addr_server, 8);
+}
+
+static void test_server_client_packet(Link *link_server, Link *link_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *incoming1 = NULL, *incoming2 = NULL;
+        _c_cleanup_(c_closep) int sk_server = -1, sk_client = -1;
+        struct in_addr addr_client = (struct in_addr){ htonl(10 << 24 | 2) };
+        struct in_addr addr_server = (struct in_addr){ htonl(10 << 24 | 1) };
+        uint8_t buf[UINT16_MAX];
+        int r;
+
+        /* setup */
+
+        link_add_ip4(link_server, &addr_server, 8);
+
+        /* test communication */
+
+        test_server_packet_socket_new(link_server, &sk_server);
+        test_client_packet_socket_new(link_client, &sk_client);
+
+        r = n_dhcp4_outgoing_new(&outgoing, 0, 0);
+        c_assert(!r);
+        n_dhcp4_outgoing_get_header(outgoing)->op = N_DHCP4_OP_BOOTREPLY;
+
+        r = n_dhcp4_s_socket_packet_send(sk_server,
+                                         link_server->ifindex,
+                                         &addr_server,
+                                         link_client->mac.ether_addr_octet,
+                                         ETH_ALEN,
+                                         &addr_client,
+                                         N_DHCP4_DSCP_DEFAULT,
+                                         outgoing);
+        c_assert(!r);
+        r = n_dhcp4_s_socket_packet_send(sk_server,
+                                         link_server->ifindex,
+                                         &addr_server,
+                                         (const unsigned char[]){
+                                                0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+                                         },
+                                         ETH_ALEN,
+                                         &addr_client,
+                                         N_DHCP4_DSCP_DEFAULT,
+                                         outgoing);
+        c_assert(!r);
+
+        test_poll(sk_client);
+
+        r = n_dhcp4_c_socket_packet_recv(sk_client, buf, sizeof(buf), &incoming1);
+        c_assert(!r);
+        c_assert(incoming1);
+
+        test_poll(sk_client);
+
+        r = n_dhcp4_c_socket_packet_recv(sk_client, buf, sizeof(buf), &incoming2);
+        c_assert(!r);
+        c_assert(incoming2);
+
+        /* teardown */
+
+        link_del_ip4(link_server, &addr_server, 8);
+}
+
+static void test_server_client_udp(Link *link_server, Link *link_client) {
+        _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL;
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *incoming = NULL;
+        _c_cleanup_(c_closep) int sk_server = -1, sk_client = -1;
+        struct in_addr addr_client = (struct in_addr){ htonl(10 << 24 | 2) };
+        struct in_addr addr_server = (struct in_addr){ htonl(10 << 24 | 1) };
+        uint8_t buf[UINT16_MAX];
+        int r;
+
+        /* setup */
+
+        link_add_ip4(link_server, &addr_server, 8);
+        link_add_ip4(link_client, &addr_client, 8);
+
+        /* test communication */
+
+        test_server_udp_socket_new(link_server, &sk_server);
+        test_client_udp_socket_new(link_client, &sk_client, &addr_client, &addr_server);
+
+        r = n_dhcp4_outgoing_new(&outgoing, 0, 0);
+        c_assert(!r);
+        n_dhcp4_outgoing_get_header(outgoing)->op = N_DHCP4_OP_BOOTREPLY;
+
+        r = n_dhcp4_s_socket_udp_send(sk_server,
+                                      &addr_server,
+                                      &addr_client,
+                                      outgoing);
+        c_assert(!r);
+
+        test_poll(sk_client);
+
+        r = n_dhcp4_c_socket_udp_recv(sk_client, buf, sizeof(buf), &incoming);
+        c_assert(!r);
+        c_assert(incoming);
+
+        /* teardown */
+
+        link_del_ip4(link_client, &addr_client, 8);
+        link_del_ip4(link_server, &addr_server, 8);
+}
+
+static void test_sockets(void) {
+        _c_cleanup_(netns_closep) int ns_server = -1, ns_client = -1;
+        _c_cleanup_(link_deinit) Link link_server = LINK_NULL(link_server);
+        _c_cleanup_(link_deinit) Link link_client = LINK_NULL(link_client);
+
+        /* setup */
+
+        netns_new(&ns_server);
+        netns_new(&ns_client);
+        link_new_veth(&link_server, &link_client, ns_server, ns_client);
+
+        /* communication tests */
+
+        test_client_server_packet(&link_server, &link_client);
+        test_client_server_udp(&link_server, &link_client);
+        test_server_client_packet(&link_server, &link_client);
+        test_server_client_udp(&link_server, &link_client);
+}
+
+static void test_multiple_servers(void) {
+        _c_cleanup_(netns_closep) int netns = -1;
+        _c_cleanup_(link_deinit) Link link_server = LINK_NULL(link_server);
+        _c_cleanup_(link_deinit) Link link_client = LINK_NULL(link_client);
+        int r, oldns;
+
+        /* setup */
+
+        netns_new(&netns);
+        link_new_veth(&link_server, &link_client, netns, netns);
+
+        /* test multiple server UDP sockets on the same machine */
+
+        netns_get(&oldns);
+        netns_set(netns);
+        {
+                _c_cleanup_(c_closep) int sk1 = -1, sk2 = -1;
+
+                /*
+                 * DHCP servers have to bind to a fixed port, so you cannot run
+                 * two servers on the same interface. It must be possible to
+                 * run them on separate interfaces, though.
+                 */
+
+                r = n_dhcp4_s_socket_udp_new(&sk1, link_server.ifindex);
+                c_assert(r >= 0);
+
+                r = n_dhcp4_s_socket_udp_new(&sk2, link_server.ifindex);
+                c_assert(r == -EADDRINUSE);
+
+                r = n_dhcp4_s_socket_udp_new(&sk2, link_client.ifindex);
+                c_assert(r >= 0);
+        }
+        netns_set(oldns);
+}
+
+int main(int argc, char **argv) {
+        test_setup();
+
+        test_sockets();
+        test_multiple_servers();
+
+        return 0;
+}
diff --git a/src/n-dhcp4/src/test.h b/src/n-dhcp4/src/test.h
new file mode 100644
index 00000000..b5acd14b
--- /dev/null
+++ b/src/n-dhcp4/src/test.h
@@ -0,0 +1,107 @@
+#pragma once
+
+/*
+ * Test Helpers
+ * Bunch of helpers to setup the environment for networking tests. This
+ * includes net-namespace setups, veth setups, and more.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static inline void test_raise_memlock(void) {
+        const size_t wanted = 64 * 1024 * 1024;
+        struct rlimit get, set;
+        int r;
+
+        r = getrlimit(RLIMIT_MEMLOCK, &get);
+        c_assert(!r);
+
+        /* try raising limit to @wanted */
+        set.rlim_cur = wanted;
+        set.rlim_max = (wanted > get.rlim_max) ? wanted : get.rlim_max;
+        r = setrlimit(RLIMIT_MEMLOCK, &set);
+        if (r) {
+                c_assert(errno == EPERM);
+
+                /* not privileged to raise limit, so maximize soft limit */
+                set.rlim_cur = get.rlim_max;
+                set.rlim_max = get.rlim_max;
+                r = setrlimit(RLIMIT_MEMLOCK, &set);
+                c_assert(!r);
+        }
+}
+
+static inline void test_unshare_user_namespace(void) {
+        uid_t euid;
+        gid_t egid;
+        int r, fd;
+
+        /*
+         * Enter a new user namespace as root:root.
+         */
+
+        euid = geteuid();
+        egid = getegid();
+
+        r = unshare(CLONE_NEWUSER);
+        c_assert(r >= 0);
+
+        fd = open("/proc/self/uid_map", O_WRONLY);
+        c_assert(fd >= 0);
+        r = dprintf(fd, "0 %d 1\n", euid);
+        c_assert(r >= 0);
+        close(fd);
+
+        fd = open("/proc/self/setgroups", O_WRONLY);
+        c_assert(fd >= 0);
+        r = dprintf(fd, "deny");
+        c_assert(r >= 0);
+        close(fd);
+
+        fd = open("/proc/self/gid_map", O_WRONLY);
+        c_assert(fd >= 0);
+        r = dprintf(fd, "0 %d 1\n", egid);
+        c_assert(r >= 0);
+        close(fd);
+}
+
+static inline void test_setup(void) {
+        int r;
+
+        /*
+         * Move into a new network and mount namespace both associated
+         * with a new user namespace where the current eUID is mapped to
+         * 0. Then create a private instance of /run/netns. This ensures
+         * that any network devices or network namespaces are private to
+         * the test process.
+         */
+
+        test_raise_memlock();
+        test_unshare_user_namespace();
+
+        r = unshare(CLONE_NEWNET | CLONE_NEWNS);
+        c_assert(r >= 0);
+
+        r = mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL);
+        c_assert(r >= 0);
+
+        r = mount(NULL, "/run", "tmpfs", 0, NULL);
+        c_assert(r >= 0);
+
+        r = mkdir("/run/netns", 0755);
+        c_assert(r >= 0);
+}
diff --git a/src/n-dhcp4/src/util/link.c b/src/n-dhcp4/src/util/link.c
new file mode 100644
index 00000000..54d9a5c3
--- /dev/null
+++ b/src/n-dhcp4/src/util/link.c
@@ -0,0 +1,287 @@
+/*
+ * Link Management
+ *
+ * This is for our test-infrastructure only! It is not meant to be used outside
+ * of unit-tests.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <c-stdaux.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "link.h"
+#include "netns.h"
+#include "socket.h"
+
+/**
+ * link_deinit() - deinitialize link
+ * @link:               link to operate on
+ *
+ * This deinitializes a link and clears it. Once this call returns the link is
+ * cleared to LINK_NULL().
+ *
+ * It is safe to call this on LINK_NULL(), in which case it is a no-op. It is
+ * thus also safe to call this multiple times on the same link.
+ */
+void link_deinit(Link *link) {
+        netns_close(link->netns);
+        *link = (Link)LINK_NULL(*link);
+}
+
+static void link_query(int netns, const char *name, int *ifindexp, struct ether_addr *macp) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                struct ifreq ifr = {};
+                size_t n_name;
+                int r, s;
+
+                netns_set(netns);
+
+                n_name = strlen(name);
+                c_assert(n_name <= IF_NAMESIZE);
+
+                if (ifindexp) {
+                        *ifindexp = if_nametoindex(name);
+                        c_assert(*ifindexp > 0);
+                }
+
+                if (macp) {
+                        s = socket(AF_INET, SOCK_DGRAM, 0);
+                        c_assert(s >= 0);
+
+                        memcpy(ifr.ifr_name, name, n_name);
+                        r = ioctl(s, SIOCGIFHWADDR, &ifr);
+                        c_assert(r >= 0);
+
+                        memcpy(macp->ether_addr_octet, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+                        close(s);
+                }
+        }
+        netns_set(oldns);
+}
+
+static void link_move(const char *ifname, int netns) {
+        char *p;
+        int r;
+
+        r = asprintf(&p, "ip link set %s up netns ns-test", ifname);
+        c_assert(r > 0);
+
+        netns_pin(netns, "ns-test");
+        r = system(p);
+        c_assert(r == 0);
+        netns_unpin("ns-test");
+
+        free(p);
+}
+
+/**
+ * link_new_veth() - create new veth pair
+ * @veth_parentp:               output argument for new veth parent
+ * @veth_childp:                output argument for new veth child
+ * @netns_parent:               target namespace for the parent
+ * @netns_child:                target namespace for the child
+ *
+ * This creates a new veth pair in the specified namespaces.
+ */
+void link_new_veth(Link *veth_parentp, Link *veth_childp, int netns_parent, int netns_child) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                int r;
+
+                /*
+                 * Temporarily enter a new network namespace to make sure the
+                 * interface names are fresh.
+                 */
+                netns_set_anonymous();
+
+                r = system("ip link add veth-parent type veth peer name veth-child");
+                c_assert(r == 0);
+                r = system("ip link set veth-parent up addrgenmode none");
+                c_assert(r == 0);
+                r = system("ip link set veth-child up addrgenmode none");
+                c_assert(r == 0);
+
+                link_move("veth-parent", netns_parent);
+                link_move("veth-child", netns_child);
+        }
+        netns_set(oldns);
+
+        netns_new_dup(&veth_parentp->netns, netns_parent);
+        netns_new_dup(&veth_childp->netns, netns_child);
+        link_query(netns_parent, "veth-parent", &veth_parentp->ifindex, &veth_parentp->mac);
+        link_query(netns_child, "veth-child", &veth_childp->ifindex, &veth_childp->mac);
+
+        /*
+         * XXX: After moving a link both its name and ifindex might have
+         *      changed. Hence, link_query() might check the wrong interface.
+         *      One way to fix this would be to rename the interfaces after
+         *      they have been moved and queried based on their final ifindex.
+         *      This way, we reserve the internal names for the constructor,
+         *      and guarantee the final names will never conflict (disallowing
+         *      parallel calls to this function).
+         */
+}
+
+/**
+ * link_new_bridge() - create new bridge
+ * @bridgep:                    output argument for the new bridge
+ * @netns:                      target network namespace
+ *
+ * This creates a new bridge interface in the specified target network
+ * namespace.
+ */
+void link_new_bridge(Link *bridgep, int netns) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                int r;
+
+                netns_set(netns);
+
+                r = system("ip link add test-bridge type bridge");
+                c_assert(r == 0);
+                r = system("ip link set test-bridge up addrgenmode none");
+                c_assert(r == 0);
+        }
+        netns_set(oldns);
+
+        netns_new_dup(&bridgep->netns, netns);
+        link_query(netns, "test-bridge", &bridgep->ifindex, &bridgep->mac);
+}
+
+/**
+ * link_add_ip4() - add IPv4 address to the specified link
+ * @link:                       link to operate on
+ * @addr:                       address to add
+ * @prefix:                     address prefix length
+ *
+ * This adds the specified IPv4 address to the given link.
+ */
+void link_add_ip4(Link *link, const struct in_addr *addr, unsigned int prefix) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                char *p, ifname[IF_NAMESIZE + 1] = {};
+                int r;
+
+                netns_set(link->netns);
+
+                p = if_indextoname(link->ifindex, ifname);
+                c_assert(p);
+                r = asprintf(&p, "ip addr add %s/%u dev %s", inet_ntoa(*addr), prefix, ifname);
+                c_assert(r >= 0);
+                r = system(p);
+                c_assert(r == 0);
+                free(p);
+        }
+        netns_set(oldns);
+}
+
+/**
+ * link_del_ip4() - delete IPv4 address from the specified link
+ * @link:                       link to operate on
+ * @addr:                       address to delete
+ * @prefix:                     address prefix length
+ *
+ * This deletes the specified IPv4 address from the given link.
+ */
+void link_del_ip4(Link *link, const struct in_addr *addr, unsigned int prefix) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                char *p, ifname[IF_NAMESIZE + 1] = {};
+                int r;
+
+                netns_set(link->netns);
+
+                p = if_indextoname(link->ifindex, ifname);
+                c_assert(p);
+                r = asprintf(&p, "ip addr del %s/%u dev %s", inet_ntoa(*addr), prefix, ifname);
+                c_assert(r >= 0);
+                r = system(p);
+                c_assert(r == 0);
+                free(p);
+        }
+        netns_set(oldns);
+}
+
+/**
+ * link_set_master() - change the bridge master of an interface
+ * @link:                       link to operate on
+ * @if_master:                  bridge to set as master
+ *
+ * This sets @if_master as the new master bridge of @link. The specified bridge
+ * must be in the same network namespace as @link.
+ */
+void link_set_master(Link *link, int if_master) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                char *p, ifname_master[IF_NAMESIZE + 1] = {}, ifname[IF_NAMESIZE + 1] = {};
+                int r;
+
+                netns_set(link->netns);
+
+                p = if_indextoname(link->ifindex, ifname);
+                c_assert(p);
+                p = if_indextoname(if_master, ifname_master);
+                c_assert(p);
+                r = asprintf(&p, "ip link set %s master %s", ifname, ifname_master);
+                c_assert(r > 0);
+                r = system(p);
+                c_assert(r == 0);
+                free(p);
+        }
+        netns_set(oldns);
+}
+
+/**
+ * link_socket() - create socket for link
+ * @link:               link to operate on
+ * @socketp:            output argument for new socket
+ * @family:             socket family to create socket in
+ * @type:               socket type to create socket as
+ *
+ * This creates a socket of the protocol family @family via socket(2), but
+ * makes sure to create it in the network-namespace where @link resides.
+ * Furthermore, the socket is bound to the link specified in @link.
+ *
+ * The new socket is returned in @socketp.
+ */
+void link_socket(Link *link, int *socketp, int family, int type) {
+        int oldns;
+
+        netns_get(&oldns);
+        {
+                int r, fd;
+
+                netns_set(link->netns);
+
+                fd = socket(family, type, 0);
+                c_assert(fd >= 0);
+
+                r = socket_bind_if(fd, link->ifindex);
+                c_assert(!r);
+
+                *socketp = fd;
+        }
+        netns_set(oldns);
+}
diff --git a/src/n-dhcp4/src/util/link.h b/src/n-dhcp4/src/util/link.h
new file mode 100644
index 00000000..cd0ad6f8
--- /dev/null
+++ b/src/n-dhcp4/src/util/link.h
@@ -0,0 +1,38 @@
+#pragma once
+
+/*
+ * Link Management
+ *
+ * This utility provides easy access to network links. It is meant for testing
+ * purposes only and relies on call-outs to ip(1). A proper implementation
+ * should rather use netlink directly to interact with the kernel.
+ *
+ * Furthermore, for simplification this is limited to ethernet links.
+ */
+
+#include <c-stdaux.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+typedef struct Link Link;
+
+struct Link {
+        int netns;
+        int ifindex;
+        struct ether_addr mac;
+};
+
+#define LINK_NULL(_x) {                                                         \
+                .netns = -1,                                                    \
+        }
+
+void link_deinit(Link *link);
+
+void link_new_veth(Link *veth_parentp, Link *veth_childp, int netns_parent, int netns_child);
+void link_new_bridge(Link *bridgep, int netns);
+
+void link_add_ip4(Link *link, const struct in_addr *addr, unsigned int prefix);
+void link_del_ip4(Link *link, const struct in_addr *addr, unsigned int prefix);
+void link_set_master(Link *link, int if_master);
+void link_socket(Link *link, int *socketp, int family, int type);
diff --git a/src/n-dhcp4/src/util/netns.c b/src/n-dhcp4/src/util/netns.c
new file mode 100644
index 00000000..0b7b32e4
--- /dev/null
+++ b/src/n-dhcp4/src/util/netns.c
@@ -0,0 +1,165 @@
+/*
+ * Network Namespaces
+ *
+ * This is meant for testing-purposes only. It is not meant to be used outside
+ * of our unit-tests!
+ */
+
+#include <assert.h>
+#include <c-stdaux.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include "netns.h"
+
+/**
+ * netns_new() - create a new network namespace
+ * @netnsp:             output argument to store netns fd
+ *
+ * This creates a new network namespace and returns a netns fd that refers to
+ * the new network namespace. Note that there is no native API to create an
+ * anonymous network namespace, so this call has to temporarily switch to a new
+ * network namespace (using unshare(2)). This temporary switch does not affect
+ * any other threads or processes, however, it can be observed by other
+ * processes.
+ */
+void netns_new(int *netnsp) {
+        int r, oldns;
+
+        netns_get(&oldns);
+
+        r = unshare(CLONE_NEWNET);
+        c_assert(r >= 0);
+
+        netns_get(netnsp);
+        netns_set(oldns);
+}
+
+/**
+ * netns_new_dup() - duplicate network namespace descriptor
+ * @newnsp:             output argument for duplicated descriptor
+ * @netns:              netns descriptor to duplicate
+ *
+ * This duplicates the network namespace file descriptor. The duplicate still
+ * refers to the same network namespace, but is an independent file descriptor.
+ */
+void netns_new_dup(int *newnsp, int netns) {
+        *newnsp = fcntl(netns, F_DUPFD_CLOEXEC, 0);
+        c_assert(*newnsp >= 0);
+}
+
+/**
+ * netns_close() - destroy a network namespace descriptor
+ * @netns:              netns to operate on, or <0
+ *
+ * This closes the given network namespace descriptor. If @netns is negative,
+ * this is a no-op.
+ *
+ * Return: -1 is returned.
+ */
+int netns_close(int netns) {
+        return c_close(netns);
+}
+
+/**
+ * netns_get() - retrieve the current network namespace
+ * @netnsp:             output argument to store netns fd
+ *
+ * This retrieves a file-descriptor to the current network namespace.
+ */
+void netns_get(int *netnsp) {
+        *netnsp = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
+        c_assert(*netnsp >= 0);
+}
+
+/**
+ * setns_set() - change the current network namespace
+ * @netns:              netns to set
+ *
+ * This changes the current network namespace to the netns given by the
+ * file-descriptor @netns.
+ */
+void netns_set(int netns) {
+        int r;
+
+        r = setns(netns, CLONE_NEWNET);
+        c_assert(r >= 0);
+}
+
+/**
+ * netns_set_anonymous() - enter an anonymous network namespace
+ *
+ * This is a helper that creates a new network namespace, enters it, and then
+ * forgets about it.
+ */
+void netns_set_anonymous(void) {
+        int r;
+
+        r = unshare(CLONE_NEWNET);
+        c_assert(r >= 0);
+}
+
+/**
+ * netns_pin() - pin network namespace in file-system
+ * @netns:              netns to pin
+ * @name:               name to pin netns under
+ *
+ * This pins the network namespace given as @netns in the file-system as
+ * `/run/netns/@name`. It is the responsibility of the caller to guarantee
+ * @name is not used by anyone else in parallel. This function will abort if
+ * @name is already in use.
+ *
+ * The namespace in `/run/netns/` is compatible with the namespace provided by
+ * the ip(1) tool, and can be used to pass network namespaces to invocations of
+ * ip(1).
+ */
+void netns_pin(int netns, const char *name) {
+        char *fd_path, *netns_path;
+        int r, fd;
+
+        r = asprintf(&fd_path, "/proc/self/fd/%d", netns);
+        c_assert(r >= 0);
+
+        r = asprintf(&netns_path, "/run/netns/%s", name);
+        c_assert(r >= 0);
+
+        fd = open(netns_path, O_RDONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0);
+        c_assert(fd >= 0);
+        close(fd);
+
+        r = mount(fd_path, netns_path, "none", MS_BIND, NULL);
+        c_assert(r >= 0);
+
+        free(netns_path);
+        free(fd_path);
+}
+
+/**
+ * netns_unpin() - unpin network namespace from file-system
+ * @name:               name to unpin
+ *
+ * This removes a network namespace pin from the file-system. It expects the
+ * pin to be located at `/run/netns/@name`. This function aborts if the pin
+ * does not exist.
+ *
+ * See netns_pin() for ways to create such pins.
+ */
+void netns_unpin(const char *name) {
+        char *netns_path;
+        int r;
+
+        r = asprintf(&netns_path, "/run/netns/%s", name);
+        c_assert(r >= 0);
+
+        r = umount2(netns_path, MNT_DETACH);
+        c_assert(r >= 0);
+
+        r = unlink(netns_path);
+        c_assert(r >= 0);
+
+        free(netns_path);
+}
diff --git a/src/n-dhcp4/src/util/netns.h b/src/n-dhcp4/src/util/netns.h
new file mode 100644
index 00000000..02e2e010
--- /dev/null
+++ b/src/n-dhcp4/src/util/netns.h
@@ -0,0 +1,27 @@
+#pragma once
+
+/*
+ * Network Namespaces
+ *
+ * The netns utility provides an object-based API to network namespaces. It is
+ * meant for testing purposes only.
+ */
+
+#include <c-stdaux.h>
+#include <stdlib.h>
+
+void netns_new(int *netnsp);
+void netns_new_dup(int *newnsp, int netns);
+int netns_close(int netns);
+
+void netns_get(int *netnsp);
+void netns_set(int netns);
+void netns_set_anonymous(void);
+
+void netns_pin(int netns, const char *name);
+void netns_unpin(const char *name);
+
+static inline void netns_closep(int *netns) {
+        if (*netns >= 0)
+                netns_close(*netns);
+}
diff --git a/src/n-dhcp4/src/util/test-packet.c b/src/n-dhcp4/src/util/test-packet.c
new file mode 100644
index 00000000..44dbc300
--- /dev/null
+++ b/src/n-dhcp4/src/util/test-packet.c
@@ -0,0 +1,411 @@
+/*
+ * Packet Socket Tests
+ */
+
+#undef NDEBUG
+#include <assert.h>
+#include <c-stdaux.h>
+#include <errno.h>
+#include <net/if_arp.h>
+#include <stdlib.h>
+#include <string.h>
+#include "n-dhcp4-private.h"
+#include "link.h"
+#include "netns.h"
+#include "packet.h"
+#include "test.h"
+
+typedef struct Blob {
+        uint16_t checksum;
+        uint8_t data[128];
+} Blob;
+
+static void test_checksum_one(Blob *blob, size_t size) {
+        uint16_t checksum;
+
+        /*
+         * The only important property of the internet-checksum is that if the
+         * target blob is amended with its own checksum, the checksum
+         * calculation will become 0. So here we simply calculate the checksum
+         * with a dummy 0 in place, then put the checksum in and verify that
+         * the resulting checksum becomes 0.
+         */
+
+        blob->checksum = 0;
+        blob->checksum = packet_internet_checksum((uint8_t*)blob, size);
+
+        checksum = packet_internet_checksum((uint8_t*)blob, size);
+        c_assert(!checksum);
+}
+
+static void test_checksum_udp_one(Blob *blob, size_t size) {
+        uint16_t checksum;
+
+        /*
+         * Like test_checksum_one(), here we calculate the target checksum,
+         * then place it in the source blob and calculate the checksum again.
+         * We expect it to be 0 in the end (i.e., pass the checksum test).
+         *
+         * Unlike the generic version, we must pass dummy UDP data into the
+         * helpers and also avoid a 0 checksum in the original source.
+         */
+
+        checksum = packet_internet_checksum_udp(&(struct in_addr){ htonl((10 << 24) | 2)},
+                                                &(struct in_addr){ htonl((10 << 24) | 1)},
+                                                67,
+                                                68,
+                                                blob->data,
+                                                sizeof(blob->data),
+                                                0);
+        checksum = checksum ?: 0xffff;
+        checksum = packet_internet_checksum_udp(&(struct in_addr){ htonl((10 << 24) | 2)},
+                                                &(struct in_addr){ htonl((10 << 24) | 1)},
+                                                67,
+                                                68,
+                                                blob->data,
+                                                sizeof(blob->data),
+                                                checksum);
+        c_assert(!checksum);
+}
+
+/*
+ * This generates some pseudo-random bytes and verifies that
+ * packet_internet_checksum{,_udp}() correctly calculates the checksum on this
+ * random-data.
+ */
+static void test_checksum(void) {
+        Blob blob = {};
+
+        /* fill @blob.data with some pseudo-random bytes */
+        for (size_t i = 0; i < sizeof(blob.data); ++i)
+                blob.data[i] = i ^ (i >> 8) ^ (i >> 16) ^ (i >> 24);
+
+        /* take chunks of @blob.data and verify their checksum */
+        for (size_t j = 0; j < sizeof(uint64_t); ++j) {
+                for (uint32_t i = 0; i <= 0xffff; ++i) {
+                        blob.data[0] = i & 0xff;
+                        blob.data[1] = i >> 8;
+                        test_checksum_one(&blob, sizeof(blob) - j);
+                        test_checksum_udp_one(&blob, sizeof(blob) - j);
+                }
+        }
+}
+
+static void test_new_packet_socket(Link *link, int *skp) {
+        struct sockaddr_ll addr = {
+                .sll_family = AF_PACKET,
+                .sll_protocol = htons(ETH_P_IP),
+                .sll_ifindex = link->ifindex,
+        };
+        int r, on = 1;
+
+        link_socket(link, skp, AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC);
+
+        r = setsockopt(*skp, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on));
+        c_assert(r >= 0);
+
+        r = bind(*skp, (struct sockaddr*)&addr, sizeof(addr));
+        c_assert(r >= 0);
+}
+
+static void test_packet_unicast(int ifindex, int sk, void *buf, size_t n_buf,
+                                const struct sockaddr_in *paddr_src,
+                                const struct sockaddr_in *paddr_dst,
+                                const struct ether_addr *haddr_dst) {
+        struct packet_sockaddr_ll addr = {
+                .sll_family = AF_PACKET,
+                .sll_protocol = htons(ETH_P_IP),
+                .sll_ifindex = ifindex,
+                .sll_halen = ETH_ALEN,
+        };
+        size_t len;
+        int r;
+
+        memcpy(addr.sll_addr, haddr_dst, ETH_ALEN);
+
+        r = packet_sendto_udp(sk, buf, n_buf, &len, paddr_src, &addr, paddr_dst, N_DHCP4_DSCP_DEFAULT);
+        c_assert(!r);
+        c_assert(len == n_buf);
+}
+
+static void test_packet_broadcast(int ifindex, int sk, void *buf, size_t n_buf,
+                                  const struct sockaddr_in *paddr_src,
+                                  const struct sockaddr_in *paddr_dst) {
+        struct packet_sockaddr_ll addr = {
+                .sll_family = AF_PACKET,
+                .sll_protocol = htons(ETH_P_IP),
+                .sll_ifindex = ifindex,
+                .sll_halen = ETH_ALEN,
+        };
+        size_t len;
+        int r;
+
+        memcpy(addr.sll_addr, (unsigned char[]){ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, ETH_ALEN);
+
+        r = packet_sendto_udp(sk, buf, n_buf, &len, paddr_src, &addr, paddr_dst, N_DHCP4_DSCP_DEFAULT);
+        c_assert(!r);
+        c_assert(len == n_buf);
+}
+
+static void test_packet_packet(Link *link_src,
+                               Link *link_dst,
+                               const struct sockaddr_in *paddr_src,
+                               const struct sockaddr_in *paddr_dst) {
+        _c_cleanup_(c_closep) int sk_src = -1, sk_dst = -1;
+        uint8_t buf[1024];
+        size_t len;
+        int r;
+
+        link_socket(link_src, &sk_src, AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC);
+        test_new_packet_socket(link_dst, &sk_dst);
+
+        test_packet_unicast(link_src->ifindex, sk_src, buf, sizeof(buf) - 1, paddr_src, paddr_dst, &link_dst->mac);
+        test_packet_broadcast(link_src->ifindex, sk_src, buf, sizeof(buf) - 1, paddr_src, paddr_dst);
+
+        r = packet_recv_udp(sk_dst, buf, sizeof(buf), &len);
+        c_assert(!r);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        r = packet_recv_udp(sk_dst, buf, sizeof(buf), &len);
+        c_assert(!r);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+}
+
+static void test_packet_udp(Link *link_src,
+                            Link *link_dst,
+                            const struct sockaddr_in *paddr_src,
+                            const struct sockaddr_in *paddr_dst) {
+        _c_cleanup_(c_closep) int sk_src = -1, sk_dst = -1;
+        uint8_t buf[1024];
+        ssize_t len;
+        int r;
+
+        link_socket(link_src, &sk_src, AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC);
+        link_socket(link_dst, &sk_dst, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+        link_add_ip4(link_dst, &paddr_dst->sin_addr, 8);
+
+        r = bind(sk_dst, (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(r >= 0);
+
+        test_packet_unicast(link_src->ifindex, sk_src, buf, sizeof(buf) - 1, paddr_src, paddr_dst, &link_dst->mac);
+        test_packet_broadcast(link_src->ifindex, sk_src, buf, sizeof(buf) - 1, paddr_src, paddr_dst);
+
+        len = recv(sk_dst, buf, sizeof(buf), 0);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        len = recv(sk_dst, buf, sizeof(buf), 0);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        link_del_ip4(link_dst, &paddr_dst->sin_addr, 8);
+}
+
+static void test_udp_packet(Link *link_src,
+                            Link *link_dst,
+                            const struct sockaddr_in *paddr_src,
+                            const struct sockaddr_in *paddr_dst) {
+        _c_cleanup_(c_closep) int sk_src = -1, sk_dst = -1;
+        uint8_t buf[1024];
+        ssize_t slen;
+        size_t len;
+        int r;
+
+        link_socket(link_src, &sk_src, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+        test_new_packet_socket(link_dst, &sk_dst);
+        link_add_ip4(link_src, &paddr_src->sin_addr, 8);
+        link_add_ip4(link_dst, &paddr_dst->sin_addr, 8);
+
+        slen = sendto(sk_src, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(slen == (ssize_t)sizeof(buf) - 1);
+
+        r = packet_recv_udp(sk_dst, buf, sizeof(buf), &len);
+        c_assert(!r);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        link_del_ip4(link_dst, &paddr_dst->sin_addr, 8);
+        link_del_ip4(link_src, &paddr_src->sin_addr, 8);
+}
+
+static void test_udp_udp(Link *link_src,
+                         Link *link_dst,
+                         const struct sockaddr_in *paddr_src,
+                         const struct sockaddr_in *paddr_dst) {
+        _c_cleanup_(c_closep) int sk_src = -1, sk_dst = -1;
+        uint8_t buf[1024];
+        ssize_t len;
+        int r;
+
+        link_socket(link_src, &sk_src, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+        link_socket(link_dst, &sk_dst, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+        link_add_ip4(link_src, &paddr_src->sin_addr, 8);
+        link_add_ip4(link_dst, &paddr_dst->sin_addr, 8);
+
+        r = bind(sk_dst, (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(r >= 0);
+
+        len = sendto(sk_src, buf, sizeof(buf) - 1, 0,
+                     (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        len = recv(sk_dst, buf, sizeof(buf), 0);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        link_del_ip4(link_dst, &paddr_dst->sin_addr, 8);
+        link_del_ip4(link_src, &paddr_src->sin_addr, 8);
+}
+
+static void test_shutdown(Link *link_src,
+                          Link *link_dst,
+                          const struct sockaddr_in *paddr_src,
+                          const struct sockaddr_in *paddr_dst) {
+        _c_cleanup_(c_closep) int sk_src = -1, sk_dst1 = -1, sk_dst2 = -1;
+        uint8_t buf[1024];
+        ssize_t slen;
+        size_t len;
+        int r;
+
+        link_socket(link_src, &sk_src, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+        test_new_packet_socket(link_dst, &sk_dst1);
+        link_add_ip4(link_src, &paddr_src->sin_addr, 8);
+        link_add_ip4(link_dst, &paddr_dst->sin_addr, 8);
+
+        /* 1 - send only to the packet socket */
+        slen = sendto(sk_src, buf, sizeof(buf), 0,
+                     (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(slen == (ssize_t)sizeof(buf));
+
+        /* create a UDP socket */
+        link_socket(link_dst, &sk_dst2, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+
+        r = bind(sk_dst2, (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(r >= 0);
+
+        /* 2 - send to both sockets */
+        slen = sendto(sk_src, buf, sizeof(buf), 0,
+                     (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(slen == (ssize_t)sizeof(buf));
+
+        /* shut down the packet socket */
+        r = packet_shutdown(sk_dst1);
+        c_assert(r >= 0);
+
+        /* 3 - send only to the UDP socket */
+        slen = sendto(sk_src, buf, sizeof(buf), 0,
+                     (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(slen == (ssize_t)sizeof(buf));
+
+        /* receive 1 and 2 on the packet socket */
+        r = packet_recv_udp(sk_dst1, buf, sizeof(buf), &len);
+        c_assert(!r);
+        c_assert(len == (ssize_t)sizeof(buf));
+        r = packet_recv_udp(sk_dst1, buf, sizeof(buf), &len);
+        c_assert(!r);
+        c_assert(len == (ssize_t)sizeof(buf));
+
+        /* make sure there is nothing more pending on the packet socket */
+        slen = recv(sk_dst1, buf, sizeof(buf), MSG_DONTWAIT);
+        c_assert(slen < 0);
+        c_assert(errno == EAGAIN);
+
+        /* receive 2 and 3 on the UDP socket */
+        slen = recv(sk_dst2, buf, sizeof(buf), 0);
+        c_assert(slen == (ssize_t)sizeof(buf));
+        slen = recv(sk_dst2, buf, sizeof(buf), 0);
+        c_assert(slen == (ssize_t)sizeof(buf));
+
+        /* make sure there is nothing more pending on the UDP socket */
+        slen = recv(sk_dst1, buf, sizeof(buf), MSG_DONTWAIT);
+        c_assert(slen < 0);
+        c_assert(errno == EAGAIN);
+
+        link_del_ip4(link_dst, &paddr_dst->sin_addr, 8);
+        link_del_ip4(link_src, &paddr_src->sin_addr, 8);
+}
+
+static void test_ip_hdr(Link *link_src,
+                        Link *link_dst,
+                        const struct sockaddr_in *paddr_src,
+                        const struct sockaddr_in *paddr_dst) {
+        _c_cleanup_(c_closep) int sk_src = -1, sk_dst = -1;
+        uint8_t ipopts[5] = { 1, 1, 1, 1, 1 };
+        uint8_t buf[1024];
+        ssize_t slen;
+        size_t len;
+        int r;
+
+        /*
+         * This test sends a packet from a UDP socket to a packet socket, but
+         * appends 5-bytes of IPOPT_NOOP ip-options. With this we verify our
+         * packet socket correctly skips additional ip-options and does not
+         * interpret the ip-header as a fixed size header.
+         */
+
+        link_socket(link_src, &sk_src, AF_INET, SOCK_DGRAM | SOCK_CLOEXEC);
+        test_new_packet_socket(link_dst, &sk_dst);
+        link_add_ip4(link_src, &paddr_src->sin_addr, 8);
+        link_add_ip4(link_dst, &paddr_dst->sin_addr, 8);
+
+        r = setsockopt(sk_src, IPPROTO_IP, IP_OPTIONS, ipopts, sizeof(ipopts));
+        c_assert(r >= 0);
+
+        slen = sendto(sk_src, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr*)paddr_dst, sizeof(*paddr_dst));
+        c_assert(slen == (ssize_t)sizeof(buf) - 1);
+
+        r = packet_recv_udp(sk_dst, buf, sizeof(buf), &len);
+        c_assert(!r);
+        c_assert(len == (ssize_t)sizeof(buf) - 1);
+
+        link_del_ip4(link_dst, &paddr_dst->sin_addr, 8);
+        link_del_ip4(link_src, &paddr_src->sin_addr, 8);
+}
+
+/*
+ * This test verifies that we can send packets from/to packet/udp sockets. It
+ * tests all combinations: packet->packet, packet->udp, udp->packet, udp->udp
+ *
+ * Furthermore, this test checks for some of the behavioural properties of our
+ * packet socket helpers.
+ */
+static void test_packet(void) {
+        _c_cleanup_(netns_closep) int ns_src = -1, ns_dst = -1;
+        _c_cleanup_(link_deinit) Link link_src = LINK_NULL(link_src);
+        _c_cleanup_(link_deinit) Link link_dst = LINK_NULL(link_dst);
+        struct sockaddr_in paddr_src = {
+                .sin_family = AF_INET,
+                .sin_addr = (struct in_addr){ htonl(10<<24 | 1) },
+                .sin_port = htons(10),
+        };
+        struct sockaddr_in paddr_dst = {
+                .sin_family = AF_INET,
+                .sin_addr = (struct in_addr){ htonl(10<<24 | 2) },
+                .sin_port = htons(11),
+        };
+
+        /* setup */
+
+        netns_new(&ns_src);
+        netns_new(&ns_dst);
+        link_new_veth(&link_src, &link_dst, ns_src, ns_dst);
+
+        /* communication tests */
+
+        test_packet_packet(&link_src, &link_dst, &paddr_src, &paddr_dst);
+        test_packet_udp(&link_src, &link_dst, &paddr_src, &paddr_dst);
+        test_udp_packet(&link_src, &link_dst, &paddr_src, &paddr_dst);
+        test_udp_udp(&link_src, &link_dst, &paddr_src, &paddr_dst);
+
+        /* behavior tests */
+
+        test_shutdown(&link_src, &link_dst, &paddr_src, &paddr_dst);
+        test_ip_hdr(&link_src, &link_dst, &paddr_src, &paddr_dst);
+}
+
+int main(int argc, char **argv) {
+        test_setup();
+
+        test_checksum();
+        test_packet();
+
+        return 0;
+}
diff --git a/src/n-dhcp4/subprojects/c-list b/src/n-dhcp4/subprojects/c-list
new file mode 120000
index 00000000..4e274698
--- /dev/null
+++ b/src/n-dhcp4/subprojects/c-list
@@ -0,0 +1 @@
+../../c-list
\ No newline at end of file
diff --git a/src/n-dhcp4/subprojects/c-siphash b/src/n-dhcp4/subprojects/c-siphash
new file mode 120000
index 00000000..70d68818
--- /dev/null
+++ b/src/n-dhcp4/subprojects/c-siphash
@@ -0,0 +1 @@
+../../c-siphash
\ No newline at end of file
diff --git a/src/nm-cloud-setup/README.md b/src/nm-cloud-setup/README.md
new file mode 100644
index 00000000..e484aa20
--- /dev/null
+++ b/src/nm-cloud-setup/README.md
@@ -0,0 +1,8 @@
+nm-cloud-setup
+==============
+
+A NetworkManager client application that aims to automatically
+configure the network in a cloud environment.
+
+See:
+- `man 8 nm-cloud-setup` ([[www]](https://networkmanager.dev/docs/api/latest/nm-cloud-setup.html))
diff --git a/src/nm-compat-headers/README.md b/src/nm-compat-headers/README.md
new file mode 100644
index 00000000..932e6890
--- /dev/null
+++ b/src/nm-compat-headers/README.md
@@ -0,0 +1,10 @@
+nm-compat-headers
+=================
+
+When we build against older system headers, we sometimes
+want to use newer features. This directory contains compat
+headers that patch the included sources with what we need.
+
+The goal is similar to linux-headers directory, but the approach
+is different. The former completely replaces system headers
+while this uses system headers and extends them.
diff --git a/src/nm-daemon-helper/README.md b/src/nm-daemon-helper/README.md
new file mode 100644
index 00000000..695f5335
--- /dev/null
+++ b/src/nm-daemon-helper/README.md
@@ -0,0 +1,11 @@
+nm-daemon-helper
+================
+
+A internal helper application that is spawned by NetworkManager
+to perform certain actions.
+
+Currently all it does is doing a reverse DNS lookup, which
+cannot be done by NetworkManager because the operation requires
+to reconfigure the libc resolver (which is a process-wide operation).
+
+This is not directly useful to the user.
diff --git a/src/nm-dispatcher/README.md b/src/nm-dispatcher/README.md
new file mode 100644
index 00000000..53c8c709
--- /dev/null
+++ b/src/nm-dispatcher/README.md
@@ -0,0 +1,15 @@
+nm-dispatcher
+=============
+
+Runs as a D-Bus activated, exit-on-idle service to execute
+user scripts (dispatcher scripts) on certain events.
+
+The user does not directly configure this service, it gets
+controlled by NetworkManager. However, the user (or other
+applications) would place scripts in certain directories for
+the dispatcher service to execute them.
+
+The systemd service is called `NetworkManager-dispatcher.service`.
+
+See:
+- `man 8 NetworkManager-dispatcher` ([[www]](https://networkmanager.dev/docs/api/latest/NetworkManager-dispatcher.html))
diff --git a/src/nm-dispatcher/nm-dispatcher.c b/src/nm-dispatcher/nm-dispatcher.c
index efb4ec00..ce252b92 100644
--- a/src/nm-dispatcher/nm-dispatcher.c
+++ b/src/nm-dispatcher/nm-dispatcher.c
@@ -306,6 +306,9 @@ build_result_options(char *stdout)
     char                          *key;
     char                          *value;
 
+    if (!stdout)
+        return NULL;
+
     lines = g_strsplit(stdout, "\n", 65);
 
     for (i = 0; lines[i] && i < 64; i++) {
diff --git a/src/nm-initrd-generator/README.md b/src/nm-initrd-generator/README.md
new file mode 100644
index 00000000..cc995f5f
--- /dev/null
+++ b/src/nm-initrd-generator/README.md
@@ -0,0 +1,12 @@
+nm-initrd-generator
+===================
+
+A command line tool that generates NetworkManager configuration.
+
+This is supposed to be run by dracut in initrd, before NetworkManager
+starts. It parses the kernel command line, generates configuration
+and quits.
+
+See:
+- `man 8 nm-initrd-generator` ([[www]](https://networkmanager.dev/docs/api/latest/nm-initrd-generator.html))
+- `man 7 dracut.cmdline`
diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c
index 1c35a905..8fcfc6d0 100644
--- a/src/nm-initrd-generator/nmi-cmdline-reader.c
+++ b/src/nm-initrd-generator/nmi-cmdline-reader.c
@@ -240,7 +240,7 @@ reader_get_connection(Reader     *reader,
             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) {
+            if (type_name == NULL && nm_setting_connection_get_controller(s_con) == NULL) {
                 connection = candidate;
                 break;
             }
diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c
index fd663b6d..1f2141e6 100644
--- a/src/nm-initrd-generator/tests/test-cmdline-reader.c
+++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c
@@ -1032,7 +1032,7 @@ test_bond(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1048,7 +1048,7 @@ test_bond(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth1");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1130,7 +1130,7 @@ test_bond_ip(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1146,7 +1146,7 @@ test_bond_ip(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth1");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1209,7 +1209,7 @@ test_bond_default(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BOND_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1285,7 +1285,7 @@ test_bridge(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1301,7 +1301,7 @@ test_bridge(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth1");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1362,7 +1362,7 @@ test_bridge_default(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1430,7 +1430,7 @@ test_bridge_ip(void)
         g_assert_cmpstr(nm_setting_connection_get_port_type(s_con),
                         ==,
                         NM_SETTING_BRIDGE_SETTING_NAME);
-        g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+        g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
         g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                         ==,
                         NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1494,7 +1494,7 @@ test_team(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth0");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_TEAM_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
@@ -1510,7 +1510,7 @@ test_team(void)
                     NM_SETTING_WIRED_SETTING_NAME);
     g_assert_cmpstr(nm_setting_connection_get_id(s_con), ==, "eth1");
     g_assert_cmpstr(nm_setting_connection_get_port_type(s_con), ==, NM_SETTING_TEAM_SETTING_NAME);
-    g_assert_cmpstr(nm_setting_connection_get_master(s_con), ==, master_uuid);
+    g_assert_cmpstr(nm_setting_connection_get_controller(s_con), ==, master_uuid);
     g_assert_cmpint(nm_setting_connection_get_multi_connect(s_con),
                     ==,
                     NM_CONNECTION_MULTI_CONNECT_SINGLE);
diff --git a/src/nm-online/README.md b/src/nm-online/README.md
new file mode 100644
index 00000000..eaa909f7
--- /dev/null
+++ b/src/nm-online/README.md
@@ -0,0 +1,14 @@
+nm-online
+=========
+
+A small NetworkManager client that blocks until
+NetworkManager is done configuring the interfaces.
+
+This is not very useful to the end user. It is used
+by `NetworkManager-wait-online.service` to determine
+when NetworkManager is done with startup.
+
+See:
+
+- `man 1 nm-online` ([[www]](https://networkmanager.dev/docs/api/latest/nm-online.html))
+- `systemctl cat NetworkManager-wait-online.service`
diff --git a/src/nm-priv-helper/README.md b/src/nm-priv-helper/README.md
new file mode 100644
index 00000000..576da7a7
--- /dev/null
+++ b/src/nm-priv-helper/README.md
@@ -0,0 +1,24 @@
+nm-priv-helper
+==============
+
+This is a D-Bus activatable, exit-on-idle service, which
+provides an internal API to NetworkManager daemon.
+
+This has no purpose for the user, it is an implementation detail
+of the daemon.
+
+The purpose is that `nm-priv-helper` can execute certain
+privileged operations which NetworkManager process is not
+allowed to. We want to sandbox NetworkManager as much as
+possible, and nm-priv-helper provides a controlled way to
+perform some very specific operations.
+
+As such, nm-priv-helper should still be sandboxed too to only
+being able to execute the operations that are necessary for
+NetworkManager.
+
+nm-priv-helper will reject all D-Bus requests that are not
+originating from the current name owner of
+"org.freedesktop.NetworkManager".  That is, it is supposed to
+only reply to NetworkManager daemon and as such is not useful to
+the user directly.
diff --git a/src/nmcli/README.md b/src/nmcli/README.md
new file mode 100644
index 00000000..a828a762
--- /dev/null
+++ b/src/nmcli/README.md
@@ -0,0 +1,13 @@
+nmcli
+=====
+
+The command line user interface of NetworkManager.
+It uses the D-Bus API of NetworkManager (via libnm).
+
+See:
+
+- `man 1 nmcli` ([[www]](https://networkmanager.dev/docs/api/latest/nmcli.html))
+- `man 7 nmcli-examples` ([[www]](https://networkmanager.dev/docs/api/latest/nmcli-examples.html))
+- `man 5 nm-settings-nmcli` ([[www]](https://networkmanager.dev/docs/api/latest/nm-settings-nmcli.html))
+
+Try also with bash-completion!
diff --git a/src/nmcli/common.h b/src/nmcli/common.h
index 1572c534..3784da35 100644
--- a/src/nmcli/common.h
+++ b/src/nmcli/common.h
@@ -81,5 +81,4 @@ extern const NmcMetaGenericInfo *const metagen_ip6_config[];
 extern const NmcMetaGenericInfo *const metagen_dhcp_config[];
 
 const char *nm_connectivity_to_string(NMConnectivityState connectivity);
-
 #endif /* NMC_COMMON_H */
diff --git a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
index 160ae32f..9008214d 100644
--- a/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
+++ b/src/nmcli/gen-metadata-nm-settings-nmcli.xml.in
@@ -58,6 +58,9 @@
         <property name="mac-address-blacklist"
                   nmcli-description="A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg &quot;00:11:22:33:44:55&quot;)."
                   format="list of MAC addresses" />
+        <property name="mac-address-denylist"
+                  nmcli-description="A list of permanent MAC addresses of Wi-Fi devices to which this connection should never apply.  Each MAC address should be given in the standard hex-digits-and-colons notation (eg &quot;00:11:22:33:44:55&quot;)."
+                  format="list of MAC addresses" />
         <property name="mac-address-randomization"
                   nmcli-description="One of &quot;default&quot; (0) (never randomize unless the user has set a global default to randomize and the supplicant supports randomization),  &quot;never&quot; (1) (never randomize the MAC address), or &quot;always&quot; (2) (always randomize the MAC address)."
                   format="choice (NMSettingMacRandomization)"
@@ -182,7 +185,7 @@
                   nmcli-description="UTF-8 encoded file path containing PAC for EAP-FAST."
                   format="string" />
         <property name="ca-cert"
-                  nmcli-description="Contains the CA certificate if used by the EAP method specified in the &quot;eap&quot; property. Certificate data is specified using a &quot;scheme&quot;; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate&apos;s DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory."
+                  nmcli-description="Contains the path to the CA certificate if used by the EAP method specified in the 802-1x.eap property. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling 802-1x.system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory."
                   format="filesystem path" />
         <property name="ca-cert-password"
                   nmcli-description="The password used to access the CA certificate stored in &quot;ca-cert&quot; property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login."
@@ -207,7 +210,7 @@
                   nmcli-description="Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server.  If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a &quot;;&quot; delimited list."
                   format="string" />
         <property name="client-cert"
-                  nmcli-description="Contains the client certificate if used by the EAP method specified in the &quot;eap&quot; property. Certificate data is specified using a &quot;scheme&quot;; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate&apos;s DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte."
+                  nmcli-description="Contains the path to the client certificate if used by the EAP method specified in the 802-1x.eap property."
                   format="filesystem path" />
         <property name="client-cert-password"
                   nmcli-description="The password used to access the client certificate stored in &quot;client-cert&quot; property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login."
@@ -241,7 +244,7 @@
                   format="string"
                   values="md5, mschapv2, otp, gtc, tls" />
         <property name="phase2-ca-cert"
-                  nmcli-description="Contains the &quot;phase 2&quot; CA certificate if used by the EAP method specified in the &quot;phase2-auth&quot; or &quot;phase2-autheap&quot; properties. Certificate data is specified using a &quot;scheme&quot;; three are currently supported: blob, path and pkcs#11 URL. When using the blob scheme this property should be set to the certificate&apos;s DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling NMSetting8021x:system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory."
+                  nmcli-description="Contains the path to the &quot;phase 2&quot; CA certificate if used by the EAP method specified in the 802-1x.phase2-auth or 802-1x.phase2-autheap properties. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended. Note that enabling 802-1x.system-ca-certs will override this setting to use the built-in path, if the built-in path is not a directory."
                   format="filesystem path" />
         <property name="phase2-ca-cert-password"
                   nmcli-description="The password used to access the &quot;phase2&quot; CA certificate stored in &quot;phase2-ca-cert&quot; property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login."
@@ -266,7 +269,7 @@
                   nmcli-description="Constraint for server domain name. If set, this list of FQDNs is used as a match requirement for dNSName element(s) of the certificate presented by the authentication server during the inner &quot;phase 2&quot; authentication. If a matching dNSName is found, this constraint is met.  If no dNSName values are present, this constraint is matched against SubjectName CN using the same comparison. Multiple valid FQDNs can be passed as a &quot;;&quot; delimited list."
                   format="string" />
         <property name="phase2-client-cert"
-                  nmcli-description="Contains the &quot;phase 2&quot; client certificate if used by the EAP method specified in the &quot;phase2-auth&quot; or &quot;phase2-autheap&quot; properties. Certificate data is specified using a &quot;scheme&quot;; two are currently supported: blob and path. When using the blob scheme (which is backwards compatible with NM 0.7.x) this property should be set to the certificate&apos;s DER encoded data. When using the path scheme, this property should be set to the full UTF-8 encoded path of the certificate, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte. This property can be unset even if the EAP method supports CA certificates, but this allows man-in-the-middle attacks and is NOT recommended."
+                  nmcli-description="Contains the path to the &quot;phase 2&quot; client certificate if used by the EAP method specified in the 802-1x.phase2-auth or 802-1x.phase2-autheap properties."
                   format="filesystem path" />
         <property name="phase2-client-cert-password"
                   nmcli-description="The password used to access the &quot;phase2&quot; client certificate stored in &quot;phase2-client-cert&quot; property. Only makes sense if the certificate is stored on a PKCS#11 token that requires a login."
@@ -290,20 +293,20 @@
                   format="flags (NMSettingSecretFlags)"
                   values="none (0x0), agent-owned (0x1), not-saved (0x2), not-required (0x4)" />
         <property name="private-key"
-                  nmcli-description="Contains the private key when the &quot;eap&quot; property is set to &quot;tls&quot;. Key data is specified using a &quot;scheme&quot;; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key&apos;s encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the &quot;private-key-password&quot; property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte, and as with the blob scheme the &quot;private-key-password&quot; property must be set to the password used to decode the PKCS#12 private key and certificate. WARNING: &quot;private-key&quot; is not a &quot;secret&quot; property, and thus unencrypted private key data using the BLOB scheme may be readable by unprivileged users.  Private keys should always be encrypted with a private key password to prevent unauthorized access to unencrypted private key data."
+                  nmcli-description="The path to the private key when the 802-1.eap property is set to &quot;tls&quot;."
                   format="filesystem path" />
         <property name="private-key-password"
-                  nmcli-description="The password used to decrypt the private key specified in the &quot;private-key&quot; property when the private key either uses the path scheme, or if the private key is a PKCS#12 format key."
+                  nmcli-description="The password used to decrypt the private key specified in the 802-1x.private-key property. This is normally used by secret agents, not directly by users."
                   format="string" />
         <property name="private-key-password-flags"
                   nmcli-description="Flags indicating how to handle the &quot;private-key-password&quot; property."
                   format="flags (NMSettingSecretFlags)"
                   values="none (0x0), agent-owned (0x1), not-saved (0x2), not-required (0x4)" />
         <property name="phase2-private-key"
-                  nmcli-description="Contains the &quot;phase 2&quot; inner private key when the &quot;phase2-auth&quot; or &quot;phase2-autheap&quot; property is set to &quot;tls&quot;. Key data is specified using a &quot;scheme&quot;; two are currently supported: blob and path. When using the blob scheme and private keys, this property should be set to the key&apos;s encrypted PEM encoded data. When using private keys with the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte. When using PKCS#12 format private keys and the blob scheme, this property should be set to the PKCS#12 data and the &quot;phase2-private-key-password&quot; property must be set to password used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files and the path scheme, this property should be set to the full UTF-8 encoded path of the key, prefixed with the string &quot;file://&quot; and ending with a terminating NUL byte, and as with the blob scheme the &quot;phase2-private-key-password&quot; property must be set to the password used to decode the PKCS#12 private key and certificate."
+                  nmcli-description="The path to the &quot;phase 2&quot; inner private key when the 802-1x.phase2-auth or 802-1x.phase2-autheap property is set to &quot;tls&quot;."
                   format="filesystem path" />
         <property name="phase2-private-key-password"
-                  nmcli-description="The password used to decrypt the &quot;phase 2&quot; private key specified in the &quot;phase2-private-key&quot; property when the private key either uses the path scheme, or is a PKCS#12 format key."
+                  nmcli-description="The password used to decrypt the &quot;phase 2&quot; private key specified in the 802-1x.phase2-private-key property. This is normally used by secret agents, not directly by users."
                   format="string" />
         <property name="phase2-private-key-password-flags"
                   nmcli-description="Flags indicating how to handle the &quot;phase2-private-key-password&quot; property."
@@ -324,6 +327,9 @@
                   nmcli-description="A timeout for the authentication. Zero means the global default; if the global default is not set, the authentication timeout is 25 seconds."
                   format="integer"
                   values="0 - 2147483647" />
+        <property name="openssl-ciphers"
+                  nmcli-description="Define openssl_ciphers for wpa_supplicant. Openssl sometimes moves ciphers among SECLEVELs, thus compiled-in default value in wpa_supplicant (as modified by some linux distributions) sometimes prevents to connect to old servers that do not support new protocols."
+                  format="string" />
     </setting>
     <setting name="802-3-ethernet"
              alias="ethernet" >
@@ -357,6 +363,9 @@
         <property name="mac-address-blacklist"
                   nmcli-description="If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55)."
                   format="list of MAC addresses" />
+        <property name="mac-address-denylist"
+                  nmcli-description="If specified, this connection will never apply to the Ethernet device whose permanent MAC address matches an address in the list.  Each MAC address is in the standard hex-digits-and-colons notation (00:11:22:33:44:55)."
+                  format="list of MAC addresses" />
         <property name="mtu"
                   alias="mtu"
                   nmcli-description="If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames."
@@ -671,13 +680,17 @@
                   format="string"
                   values="bond, bridge, ovs-bridge, ovs-port, team, vrf" />
         <property name="autoconnect-slaves"
-                  nmcli-description="Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties &quot;autoconnect&quot;, &quot;autoconnect-priority&quot; and &quot;autoconnect-retries&quot; are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0."
+                  nmcli-description="Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties &quot;autoconnect&quot;, &quot;autoconnect-priority&quot; and &quot;autoconnect-retries&quot; are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0. Deprecated 1.46. Use &quot;autoconnect-ports&quot; instead, this is just an alias."
                   format="choice (NMSettingConnectionAutoconnectSlaves)"
                   values="default (-1), no (0), yes (1)" />
         <property name="autoconnect-ports"
                   nmcli-description="Whether or not ports of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for controller connections. The properties &quot;autoconnect&quot;, &quot;autoconnect-priority&quot; and &quot;autoconnect-retries&quot; are unrelated to this setting. The permitted values are: 0: leave port connections untouched, 1: activate all the port connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-ports is read to determine the real value. If it is default as well, this fallbacks to 0."
                   format="choice (NMTernary)"
                   values="default (-1), false (0), true (1)" />
+        <property name="down-on-poweroff"
+                  nmcli-description="Whether the connection will be brought down before the system is powered off.  The default value is &quot;default&quot; (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as &quot;no&quot; (0)."
+                  format="ternary"
+                  values="true/yes/on, false/no/off, default/unknown" />
         <property name="secondaries"
                   nmcli-description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported."
                   format="list of strings" />
@@ -694,7 +707,7 @@
                   format="choice (NMSettingConnectionLldp)"
                   values="default (-1), disable (0), enable-rx/enable (1)" />
         <property name="mdns"
-                  nmcli-description="Whether mDNS is enabled for the connection. The permitted values are: &quot;yes&quot; (2) register hostname and resolving for the connection, &quot;no&quot; (0) disable mDNS for the interface, &quot;resolve&quot; (1) do not register hostname but allow resolving of mDNS host names and &quot;default&quot; (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, &quot;default&quot; ultimately depends on the DNS plugin (which for systemd-resolved currently means &quot;no&quot;). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved."
+                  nmcli-description="Whether mDNS is enabled for the connection. The permitted values are: &quot;yes&quot; (2) register hostname and resolving for the connection, &quot;no&quot; (0) disable mDNS for the interface, &quot;resolve&quot; (1) do not register hostname but allow resolving of mDNS host names and &quot;default&quot; (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, &quot;default&quot; ultimately depends on the DNS plugin. This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. Currently the only supported DNS plugin is systemd-resolved. For systemd-resolved, the default is configurable via MulticastDNS= setting in resolved.conf."
                   format="choice (NMSettingConnectionMdns)"
                   values="default (-1), no (0), resolve (1), yes (2)" />
         <property name="llmnr"
@@ -1297,6 +1310,10 @@
                   nmcli-description="Connections will default to keep the autogenerated priority 0 local rule unless this setting is set to TRUE."
                   format="ternary"
                   values="true/yes/on, false/no/off, default/unknown" />
+        <property name="dhcp-send-release"
+                  nmcli-description="Whether the DHCP client will send RELEASE message when bringing the connection down. The default value is &quot;default&quot; (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as FALSE."
+                  format="ternary"
+                  values="true/yes/on, false/no/off, default/unknown" />
         <property name="ignore-auto-routes"
                   nmcli-description="When &quot;method&quot; is set to &quot;auto&quot; and this property to TRUE, automatically configured routes are ignored and only routes specified in the &quot;routes&quot; property, if any, are used."
                   format="boolean"
@@ -1411,6 +1428,10 @@
                   nmcli-description="Connections will default to keep the autogenerated priority 0 local rule unless this setting is set to TRUE."
                   format="ternary"
                   values="true/yes/on, false/no/off, default/unknown" />
+        <property name="dhcp-send-release"
+                  nmcli-description="Whether the DHCP client will send RELEASE message when bringing the connection down. The default value is &quot;default&quot; (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as FALSE."
+                  format="ternary"
+                  values="true/yes/on, false/no/off, default/unknown" />
         <property name="ignore-auto-routes"
                   nmcli-description="When &quot;method&quot; is set to &quot;auto&quot; and this property to TRUE, automatically configured routes are ignored and only routes specified in the &quot;routes&quot; property, if any, are used."
                   format="boolean"
@@ -1433,9 +1454,17 @@
                   values="-1 - 2147483647"
                   special-values="default (-1), infinity (2147483647)" />
         <property name="ip6-privacy"
-                  nmcli-description="Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). Having a per-connection setting set to &quot;-1&quot; (unknown) means fallback to global configuration &quot;ipv6.ip6-privacy&quot;. If also global configuration is unspecified or set to &quot;-1&quot;, fallback to read &quot;/proc/sys/net/ipv6/conf/default/use_tempaddr&quot;. Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the &quot;addr-gen-mode&quot; property&apos;s &quot;stable-privacy&quot; setting as another way of avoiding host tracking with IPv6 addresses."
+                  nmcli-description="Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941.  If enabled, it makes the kernel generate a temporary IPv6 address in addition to the public one generated from MAC address via modified EUI-64.  This enhances privacy, but could cause problems in some applications, on the other hand.  The permitted values are: -1: unknown, 0: disabled, 1: enabled (prefer public address), 2: enabled (prefer temporary addresses). If set to &quot;-1&quot; (unknown) for a connection, the value is taken from the global &quot;ipv6.ip6-privacy&quot; setting. If the global setting is unspecified or also set to &quot;-1&quot;, the value is set from the original value of &quot;/proc/sys/net/ipv6/conf/&lt;iface&gt;/use_tempaddr&quot; from before NetworkManager started. Note that this setting is distinct from the Stable Privacy addresses that can be enabled with the &quot;addr-gen-mode&quot; property&apos;s &quot;stable-privacy&quot; setting as another way of avoiding host tracking with IPv6 addresses."
                   format="choice (NMSettingIP6ConfigPrivacy)"
                   values="unknown (-1), disabled (0), prefer-public-addr (1), prefer-temp-addr (2)" />
+        <property name="temp-valid-lifetime"
+                  nmcli-description="The valid lifetime of autogenerated temporary addresses, in seconds. If set to &quot;0&quot; (unknown) for a connection, the value is taken from the global &quot;ipv6.temp-valid-lifetime&quot; setting. If the global setting is unspecified or also set to &quot;0&quot;, the value is set from the original value of &quot;/proc/sys/net/ipv6/conf/&lt;iface&gt;/temp_valid_lft&quot; from before NetworkManager started."
+                  format="integer"
+                  values="0 - 2147483647" />
+        <property name="temp-preferred-lifetime"
+                  nmcli-description="The preferred lifetime of autogenerated temporary addresses, in seconds. If set to &quot;0&quot; (unknown) for a connection, the value is taken from the global &quot;ipv6.temp-preferred-lifetime&quot; setting. If the global setting is unspecified or also set to &quot;0&quot;, the value is set from the original value of &quot;/proc/sys/net/ipv6/conf/&lt;iface&gt;/temp_prefered_lft&quot; from before NetworkManager started."
+                  format="integer"
+                  values="0 - 2147483647" />
         <property name="addr-gen-mode"
                   nmcli-description="Configure method for creating the IPv6 interface identifer of addresses with RFC4862 IPv6 Stateless Address Autoconfiguration and Link Local addresses. The permitted values are: &quot;eui64&quot; (0), &quot;stable-privacy&quot; (1), &quot;default&quot; (3) or &quot;default-or-eui64&quot; (2). If the property is set to &quot;eui64&quot;, the addresses will be generated using the interface token derived from hardware address. This makes the host part of the address to stay constant, making it possible to track the host&apos;s presence when it changes networks. The address changes when the interface hardware is replaced. If a duplicate address is detected, there is also no fallback to generate another address. When configured, the &quot;ipv6.token&quot; is used instead of the MAC address to generate addresses for stateless autoconfiguration. If the property is set to &quot;stable-privacy&quot;, the interface identifier is generated as specified by RFC7217. This works by hashing a host specific key (see NetworkManager(8) manual), the interface name, the connection&apos;s &quot;connection.stable-id&quot; property and the address prefix.  This improves privacy by making it harder to use the address to track the host&apos;s presence and the address is stable when the network interface hardware is replaced. The special values &quot;default&quot; and &quot;default-or-eui64&quot; will fallback to the global connection default as documented in the NetworkManager.conf(5) manual. If the global default is not specified, the fallback value is &quot;stable-privacy&quot; or &quot;eui64&quot;, respectively. If not specified, when creating a new profile the default is &quot;default&quot;. Note that this setting is distinct from the Privacy Extensions as configured by &quot;ip6-privacy&quot; property and it does not affect the temporary addresses configured with this option."
                   format="choice (NMSettingIP6ConfigAddrGenMode)"
diff --git a/src/nmcli/general.c b/src/nmcli/general.c
index 6fc8184c..adb3e745 100644
--- a/src/nmcli/general.c
+++ b/src/nmcli/general.c
@@ -116,6 +116,7 @@ _metagen_general_status_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS)
     gboolean            v_bool;
     NMState             state;
     NMConnectivityState connectivity;
+    NMMetered           metered;
 
     switch (info->info_type) {
     case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_RUNNING:
@@ -170,6 +171,11 @@ _metagen_general_status_get_fcn(NMC_META_GENERIC_INFO_GET_FCN_ARGS)
     case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX:
         /* deprecated fields. Don't return anything. */
         return NULL;
+    case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_METERED:
+        metered = nm_client_get_metered(nmc->client);
+        NMC_HANDLE_COLOR(NM_META_COLOR_NONE);
+        value = nmc_device_metered_to_string(metered);
+        goto translate_and_out;
     default:
         break;
     }
@@ -206,12 +212,13 @@ static const NmcMetaGenericInfo
         _METAGEN_GENERAL_STATUS(NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WWAN, "WWAN"),
         _METAGEN_GENERAL_STATUS(NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX_HW, "WIMAX-HW"),
         _METAGEN_GENERAL_STATUS(NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX, "WIMAX"),
+        _METAGEN_GENERAL_STATUS(NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_METERED, "METERED"),
 };
 #define NMC_FIELDS_NM_STATUS_ALL \
-    "RUNNING,VERSION,STATE,STARTUP,CONNECTIVITY,NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN"
+    "RUNNING,VERSION,STATE,STARTUP,CONNECTIVITY,NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN,METERED"
 #define NMC_FIELDS_NM_STATUS_SWITCH "NETWORKING,WIFI-HW,WIFI,WWAN-HW,WWAN"
 #define NMC_FIELDS_NM_STATUS_RADIO  "WIFI-HW,WIFI,WWAN-HW,WWAN"
-#define NMC_FIELDS_NM_STATUS_COMMON "STATE,CONNECTIVITY,WIFI-HW,WIFI,WWAN-HW,WWAN"
+#define NMC_FIELDS_NM_STATUS_COMMON "STATE,CONNECTIVITY,WIFI-HW,WIFI,WWAN-HW,WWAN,METERED"
 #define NMC_FIELDS_NM_NETWORKING    "NETWORKING"
 #define NMC_FIELDS_NM_WIFI          "WIFI"
 #define NMC_FIELDS_NM_WWAN          "WWAN"
diff --git a/src/nmcli/settings.c b/src/nmcli/settings.c
index d6691125..636df466 100644
--- a/src/nmcli/settings.c
+++ b/src/nmcli/settings.c
@@ -261,7 +261,7 @@ connection_controller_changed_cb(GObject *object, GParamSpec *pspec, gpointer us
     NMSetting           *s_ipv4, *s_ipv6;
     const char          *value, *tmp_str;
 
-    value = nm_setting_connection_get_master(s_con);
+    value = nm_setting_connection_get_controller(s_con);
     if (value) {
         s_ipv4 = nm_connection_get_setting_by_name(connection, NM_SETTING_IP4_CONFIG_SETTING_NAME);
         s_ipv6 = nm_connection_get_setting_by_name(connection, NM_SETTING_IP6_CONFIG_SETTING_NAME);
diff --git a/src/nmcli/utils.h b/src/nmcli/utils.h
index 2f7617b9..b68a3803 100644
--- a/src/nmcli/utils.h
+++ b/src/nmcli/utils.h
@@ -93,6 +93,7 @@ typedef enum {
     NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WWAN,
     NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX_HW,
     NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX,
+    NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_METERED,
     _NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_NUM,
 
     NMC_GENERIC_INFO_TYPE_GENERAL_PERMISSIONS_PERMISSION = 0,
diff --git a/src/nmtui/README.md b/src/nmtui/README.md
new file mode 100644
index 00000000..5205f480
--- /dev/null
+++ b/src/nmtui/README.md
@@ -0,0 +1,15 @@
+nmtui
+=====
+
+The curses-based text user interface of NetworkManager.
+It uses the D-Bus API of NetworkManager (via libnm).
+
+This is a NetworkManager client applications that can
+edit connection profiles and activate them, by providing
+a text UI.
+
+It uses libnewt.
+
+See:
+
+- `man 1 nmtui` ([[www]](https://networkmanager.dev/docs/api/latest/nmtui.html))
diff --git a/src/nmtui/nmt-8021x-fields.c b/src/nmtui/nmt-8021x-fields.c
index a323be62..62ab69d8 100644
--- a/src/nmtui/nmt-8021x-fields.c
+++ b/src/nmtui/nmt-8021x-fields.c
@@ -30,6 +30,7 @@ struct _EapMethod {
     const EapMethodDesc *desc;
     NMSetting8021x      *setting;
     NmtNewtWidget       *inner_popup;
+    NmtNewtWidget       *advanced_tls_settings;
 };
 
 typedef struct {
@@ -196,6 +197,166 @@ eap_method_populate_simple(EapMethod *method, NmtNewtWidget *subgrid)
 }
 
 static void
+checkbox_advanced_tls_settings_changed(NmtNewtWidget *widget, GParamSpec *pspec, gpointer user_data)
+{
+    EapMethod *method = (EapMethod *) (user_data);
+    gboolean   active;
+
+    active = nmt_newt_checkbox_get_active(NMT_NEWT_CHECKBOX(widget));
+    nmt_newt_widget_set_visible(method->advanced_tls_settings, active);
+}
+
+static NmtNewtPopupEntry pe_tristate[] = {{N_("Default"), ""},
+                                          {N_("Disable"), "disable"},
+                                          {N_("Enable"), "enable"},
+                                          {NULL, NULL}};
+
+static NmtNewtPopupEntry pe_disable[] = {{N_("Default"), ""},
+                                         {N_("Disable"), "disable"},
+                                         {NULL, NULL}};
+
+typedef struct _AuthFlagsTls AuthFlagsTls;
+struct _AuthFlagsTls {
+    const char             *label;
+    NMSetting8021xAuthFlags disable;
+    NMSetting8021xAuthFlags enable;
+    NmtNewtPopupEntry      *states;
+};
+
+static gboolean
+phase1_auth_flag_get_bit(GBinding     *binding,
+                         const GValue *from_value,
+                         GValue       *to_value,
+                         gpointer      user_data)
+{
+    AuthFlagsTls *af  = (AuthFlagsTls *) user_data;
+    unsigned      src = g_value_get_uint(from_value);
+
+    if (src & af->disable)
+        g_value_set_string(to_value, "disable");
+    else if (src & af->enable)
+        g_value_set_string(to_value, "enable");
+    else
+        g_value_set_string(to_value, NULL);
+    return TRUE;
+}
+
+static gboolean
+phase1_auth_flag_set_bit(GBinding     *binding,
+                         const GValue *from_value,
+                         GValue       *to_value,
+                         gpointer      user_data)
+{
+    AuthFlagsTls *af          = (AuthFlagsTls *) user_data;
+    GObject      *from_object = g_binding_get_source(binding);
+    GValue        orig_value;
+    unsigned      bits;
+    const char   *str_value;
+
+    if (from_object == NULL)
+        return FALSE;
+    g_value_init(&orig_value, G_TYPE_UINT);
+    g_object_get_property(from_object, g_binding_get_source_property(binding), &orig_value);
+    bits = g_value_get_uint(&orig_value);
+    g_value_unset(&orig_value);
+    str_value = g_value_get_string(from_value);
+    bits      = bits & ~(unsigned) (af->enable | af->disable);
+    if (str_value)
+        switch (str_value[0]) {
+        case 'd':
+            bits |= af->disable;
+            break;
+        case 'e':
+            bits |= af->enable;
+            break;
+        }
+    g_value_set_uint(to_value, bits);
+    return TRUE;
+}
+
+static gboolean
+empty_string_to_null(GBinding     *binding,
+                     const GValue *from_value,
+                     GValue       *to_value,
+                     gpointer      _unused)
+{
+    const char *value = g_value_get_string(from_value);
+
+    if (value && value[0])
+        g_value_set_string(to_value, value);
+    return TRUE;
+}
+
+static void
+eap_populate_advanced_tls_settings(EapMethod *method, NmtNewtWidget *subgrid)
+{
+    NmtNewtWidget      *widget, *tlsgrid;
+    gboolean            has_advanced_settings;
+    const char         *oc;
+    static AuthFlagsTls auth_flag_switches[] = {
+#define ENDIS_TLS(name, proto)                       \
+    {name,                                           \
+     NM_SETTING_802_1X_AUTH_FLAGS_##proto##_DISABLE, \
+     NM_SETTING_802_1X_AUTH_FLAGS_##proto##_ENABLE,  \
+     pe_tristate}
+        ENDIS_TLS(N_("TLS 1.0"), TLS_1_0),
+        ENDIS_TLS(N_("TLS 1.1"), TLS_1_1),
+        ENDIS_TLS(N_("TLS 1.2"), TLS_1_2),
+        ENDIS_TLS(N_("TLS 1.3"), TLS_1_3),
+#undef ENDIS_TLS
+        {N_("Disable time checks"),
+         NM_SETTING_802_1X_AUTH_FLAGS_TLS_DISABLE_TIME_CHECKS,
+         NM_SETTING_802_1X_AUTH_FLAGS_NONE,
+         pe_disable},
+        {NULL, NM_SETTING_802_1X_AUTH_FLAGS_NONE, NM_SETTING_802_1X_AUTH_FLAGS_NONE, NULL},
+    };
+
+    oc = nm_setting_802_1x_get_openssl_ciphers(method->setting);
+    has_advanced_settings =
+        (oc != NULL && !!strlen(oc)) || !!nm_setting_802_1x_get_phase1_auth_flags(method->setting);
+
+    widget = nmt_newt_checkbox_new(_("Show expert TLS options"));
+    nmt_newt_checkbox_set_active(NMT_NEWT_CHECKBOX(widget), has_advanced_settings);
+    g_signal_connect(widget,
+                     "notify::active",
+                     G_CALLBACK(checkbox_advanced_tls_settings_changed),
+                     method);
+    nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), NULL, widget, NULL);
+
+    tlsgrid                       = nmt_editor_grid_new();
+    method->advanced_tls_settings = tlsgrid;
+    nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), NULL, tlsgrid, NULL);
+    checkbox_advanced_tls_settings_changed(widget, NULL, method);
+
+    nmt_editor_grid_append(NMT_EDITOR_GRID(tlsgrid), _("Wpa_supplicant settings:"), NULL, NULL);
+    widget = nmt_newt_entry_new(40, 0);
+    nmt_editor_grid_append(NMT_EDITOR_GRID(tlsgrid), _("Cipher string"), widget, NULL);
+    g_object_bind_property_full(method->setting,
+                                NM_SETTING_802_1X_OPENSSL_CIPHERS,
+                                widget,
+                                "text",
+                                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+                                NULL,
+                                empty_string_to_null,
+                                NULL,
+                                NULL);
+
+    for (AuthFlagsTls *sw = auth_flag_switches; sw->states; sw++) {
+        widget = nmt_newt_popup_new(sw->states);
+        nmt_editor_grid_append(NMT_EDITOR_GRID(tlsgrid), sw->label, widget, NULL);
+        g_object_bind_property_full(method->setting,
+                                    NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
+                                    widget,
+                                    "active-id",
+                                    G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+                                    phase1_auth_flag_get_bit,
+                                    phase1_auth_flag_set_bit,
+                                    (gpointer) sw,
+                                    NULL);
+    }
+}
+
+static void
 eap_method_populate_tls(EapMethod *method, NmtNewtWidget *subgrid)
 {
     NmtNewtWidget *widget;
@@ -282,6 +443,8 @@ eap_method_populate_tls(EapMethod *method, NmtNewtWidget *subgrid)
                            "password",
                            G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
     nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("User privkey password"), widget, NULL);
+
+    eap_populate_advanced_tls_settings(method, subgrid);
 }
 
 static void
@@ -297,7 +460,7 @@ eap_method_populate_ttls(EapMethod *method, NmtNewtWidget *subgrid)
                                               {N_("GTC"), "eap-gtc"},
                                               {NULL, NULL}};
 
-    widget = nmt_newt_entry_new(40, NMT_NEWT_ENTRY_NONEMPTY);
+    widget = nmt_newt_entry_new(40, 0);
     nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Anonymous identity"), widget, NULL);
     g_object_bind_property(method->setting,
                            NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
@@ -371,6 +534,8 @@ eap_method_populate_ttls(EapMethod *method, NmtNewtWidget *subgrid)
                            "secret-flags",
                            G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
     nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Password"), widget, NULL);
+
+    eap_populate_advanced_tls_settings(method, subgrid);
 }
 
 static void
@@ -386,7 +551,7 @@ eap_method_populate_peap(EapMethod *method, NmtNewtWidget *subgrid)
                                                        {N_("GTC"), "gtc"},
                                                        {NULL, NULL}};
 
-    widget = nmt_newt_entry_new(40, NMT_NEWT_ENTRY_NONEMPTY);
+    widget = nmt_newt_entry_new(40, 0);
     nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Anonymous identity"), widget, NULL);
     g_object_bind_property(method->setting,
                            NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
@@ -475,6 +640,8 @@ eap_method_populate_peap(EapMethod *method, NmtNewtWidget *subgrid)
                            "secret-flags",
                            G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
     nmt_editor_grid_append(NMT_EDITOR_GRID(subgrid), _("Password"), widget, NULL);
+
+    eap_populate_advanced_tls_settings(method, subgrid);
 }
 
 static void
diff --git a/src/nmtui/nmt-connect-connection-list.c b/src/nmtui/nmt-connect-connection-list.c
index 329494cc..70264d3e 100644
--- a/src/nmtui/nmt-connect-connection-list.c
+++ b/src/nmtui/nmt-connect-connection-list.c
@@ -184,7 +184,7 @@ add_connections_for_device(NmtConnectDevice *nmtdev, const GPtrArray *connection
         NMSettingConnection *s_con;
 
         s_con = nm_connection_get_setting_connection(conn);
-        if (nm_setting_connection_get_master(s_con))
+        if (nm_setting_connection_get_controller(s_con))
             continue;
 
         if (nm_device_connection_valid(nmtdev->device, conn)) {
diff --git a/src/nmtui/nmt-port-list.c b/src/nmtui/nmt-port-list.c
index 9e4dd8a6..aa3217b1 100644
--- a/src/nmtui/nmt-port-list.c
+++ b/src/nmtui/nmt-port-list.c
@@ -107,7 +107,7 @@ nmt_port_list_connection_filter(NmtEditConnectionList *list,
     if (g_strcmp0(port_type, priv->controller_type) != 0)
         return FALSE;
 
-    controller = nm_setting_connection_get_master(s_con);
+    controller = nm_setting_connection_get_controller(s_con);
     if (!controller)
         return FALSE;
 
diff --git a/src/nmtui/nmtui-edit.c b/src/nmtui/nmtui-edit.c
index 723cfea9..0ba3bd0d 100644
--- a/src/nmtui/nmtui-edit.c
+++ b/src/nmtui/nmtui-edit.c
@@ -58,7 +58,7 @@ edit_connection_list_filter(NmtEditConnectionList *list,
     s_con = nm_connection_get_setting_connection(connection);
     g_return_val_if_fail(s_con != NULL, FALSE);
 
-    controller = nm_setting_connection_get_master(s_con);
+    controller = nm_setting_connection_get_controller(s_con);
     if (!controller)
         return TRUE;
     port_type = nm_setting_connection_get_port_type(s_con);
@@ -525,7 +525,7 @@ nmt_remove_connection(NMRemoteConnection *connection)
     for (i = 0; i < all_conns->len; i++) {
         port       = all_conns->pdata[i];
         s_con      = nm_connection_get_setting_connection(NM_CONNECTION(port));
-        controller = nm_setting_connection_get_master(s_con);
+        controller = nm_setting_connection_get_controller(s_con);
         if (controller) {
             if (!g_strcmp0(controller, uuid) || !g_strcmp0(controller, iface))
                 ports = g_slist_prepend(ports, g_object_ref(port));
diff --git a/src/tests/README.md b/src/tests/README.md
new file mode 100644
index 00000000..7ffae40d
--- /dev/null
+++ b/src/tests/README.md
@@ -0,0 +1,8 @@
+tests
+=====
+
+Contains some unit tests that don't clearly fit a category
+to be placed in a different location.
+
+Maybe a better place should be found and this directory should
+go away.
diff --git a/src/tests/client/meson.build b/src/tests/client/meson.build
index 8c36e405..5686a1c1 100644
--- a/src/tests/client/meson.build
+++ b/src/tests/client/meson.build
@@ -6,7 +6,7 @@ test(
   args: [
     build_root,
     source_root,
-    python.path(),
+    python_path,
     '--',
     'TestNmcli',
   ],
@@ -23,7 +23,7 @@ if enable_nm_cloud_setup
     args: [
       build_root,
       source_root,
-      python.path(),
+      python_path,
       '--',
       'TestNmCloudSetup',
     ],
diff --git a/src/tests/client/terminal-colors.d/nmcli.enable b/src/tests/client/terminal-colors.d/nmcli.enable
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/tests/client/terminal-colors.d/nmcli.enable
diff --git a/src/tests/client/terminal-colors.d/nmcli.schem b/src/tests/client/terminal-colors.d/nmcli.schem
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/tests/client/terminal-colors.d/nmcli.schem
diff --git a/src/tests/client/test-client.check-on-disk/test_002.expected b/src/tests/client/test-client.check-on-disk/test_002.expected
index 680de9a1..9da93de3 100644
--- a/src/tests/client/test-client.check-on-disk/test_002.expected
+++ b/src/tests/client/test-client.check-on-disk/test_002.expected
@@ -502,12 +502,12 @@ NAME   UUID                                  TYPE      DEVICE
 con-1  5fcfd6d7-1e63-3332-8826-a7eda103792d  ethernet  --     
 
 <<<
-size: 1512
+size: 1565
 location: src/tests/client/test-client.py:test_002()/23
 cmd: $NMCLI c s con-1
 lang: C
 returncode: 0
-stdout: 1384 bytes
+stdout: 1437 bytes
 >>>
 connection.id:                          con-1
 connection.uuid:                        5fcfd6d7-1e63-3332-8826-a7eda103792d
@@ -528,6 +528,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -540,12 +541,12 @@ connection.wait-device-timeout:         -1
 connection.wait-activation-delay:       -1
 
 <<<
-size: 1523
+size: 1576
 location: src/tests/client/test-client.py:test_002()/24
 cmd: $NMCLI c s con-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 1385 bytes
+stdout: 1438 bytes
 >>>
 connection.id:                          con-1
 connection.uuid:                        5fcfd6d7-1e63-3332-8826-a7eda103792d
@@ -566,6 +567,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
diff --git a/src/tests/client/test-client.check-on-disk/test_003.expected b/src/tests/client/test-client.check-on-disk/test_003.expected
index d55142d7..3fc65fba 100644
--- a/src/tests/client/test-client.check-on-disk/test_003.expected
+++ b/src/tests/client/test-client.check-on-disk/test_003.expected
@@ -182,12 +182,12 @@ id
 path
 uuid
 <<<
-size: 5364
+size: 5607
 location: src/tests/client/test-client.py:test_003()/14
 cmd: $NMCLI con s con-gsm1
 lang: C
 returncode: 0
-stdout: 5231 bytes
+stdout: 5474 bytes
 >>>
 connection.id:                          con-gsm1
 connection.uuid:                        UUID-con-gsm1-REPLACED-REPLACED-REPL
@@ -208,6 +208,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -230,6 +231,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -260,12 +262,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -305,12 +310,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 5402
+size: 5645
 location: src/tests/client/test-client.py:test_003()/15
 cmd: $NMCLI con s con-gsm1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5259 bytes
+stdout: 5502 bytes
 >>>
 connection.id:                          con-gsm1
 connection.uuid:                        UUID-con-gsm1-REPLACED-REPLACED-REPL
@@ -331,6 +336,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -353,6 +359,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -383,12 +390,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -428,42 +438,42 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 513
+size: 526
 location: src/tests/client/test-client.py:test_003()/16
 cmd: $NMCLI -g all con s con-gsm1
 lang: C
 returncode: 0
-stdout: 374 bytes
+stdout: 387 bytes
 >>>
-connection:con-gsm1:UUID-con-gsm1-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::: :0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-gsm1:UUID-con-gsm1-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::: :0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 serial:5:8:even:1:100
 gsm:no:::<hidden>:0:xyz.con-gsm1::<hidden>:0:no::::auto:no:
 proxy:none:no::
 
 <<<
-size: 523
+size: 536
 location: src/tests/client/test-client.py:test_003()/17
 cmd: $NMCLI -g all con s con-gsm1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 374 bytes
+stdout: 387 bytes
 >>>
-connection:con-gsm1:UUID-con-gsm1-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::: :0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-gsm1:UUID-con-gsm1-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::: :0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 serial:5:8:even:1:100
 gsm:no:::<hidden>:0:xyz.con-gsm1::<hidden>:0:no::::auto:no:
 proxy:none:no::
 
 <<<
-size: 5352
+size: 5595
 location: src/tests/client/test-client.py:test_003()/18
 cmd: $NMCLI con s con-gsm2
 lang: C
 returncode: 0
-stdout: 5219 bytes
+stdout: 5462 bytes
 >>>
 connection.id:                          con-gsm2
 connection.uuid:                        UUID-con-gsm2-REPLACED-REPLACED-REPL
@@ -484,6 +494,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -506,6 +517,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -536,12 +548,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -581,12 +596,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 5390
+size: 5633
 location: src/tests/client/test-client.py:test_003()/19
 cmd: $NMCLI con s con-gsm2
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5247 bytes
+stdout: 5490 bytes
 >>>
 connection.id:                          con-gsm2
 connection.uuid:                        UUID-con-gsm2-REPLACED-REPLACED-REPL
@@ -607,6 +622,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -629,6 +645,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -659,12 +676,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -704,42 +724,42 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 501
+size: 514
 location: src/tests/client/test-client.py:test_003()/20
 cmd: $NMCLI -g all con s con-gsm2
 lang: C
 returncode: 0
-stdout: 362 bytes
+stdout: 375 bytes
 >>>
-connection:con-gsm2:UUID-con-gsm2-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::: :0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-gsm2:UUID-con-gsm2-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::: :0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 serial:5:8:even:1:100
 gsm:no:::<hidden>:0:::<hidden>:0:no::::auto:no:
 proxy:none:no::
 
 <<<
-size: 511
+size: 524
 location: src/tests/client/test-client.py:test_003()/21
 cmd: $NMCLI -g all con s con-gsm2
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 362 bytes
+stdout: 375 bytes
 >>>
-connection:con-gsm2:UUID-con-gsm2-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::: :0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-gsm2:UUID-con-gsm2-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::: :0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 serial:5:8:even:1:100
 gsm:no:::<hidden>:0:::<hidden>:0:no::::auto:no:
 proxy:none:no::
 
 <<<
-size: 5352
+size: 5595
 location: src/tests/client/test-client.py:test_003()/22
 cmd: $NMCLI con s con-gsm3
 lang: C
 returncode: 0
-stdout: 5219 bytes
+stdout: 5462 bytes
 >>>
 connection.id:                          con-gsm3
 connection.uuid:                        UUID-con-gsm3-REPLACED-REPLACED-REPL
@@ -760,6 +780,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -782,6 +803,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -812,12 +834,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -857,12 +882,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 5390
+size: 5633
 location: src/tests/client/test-client.py:test_003()/23
 cmd: $NMCLI con s con-gsm3
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5247 bytes
+stdout: 5490 bytes
 >>>
 connection.id:                          con-gsm3
 connection.uuid:                        UUID-con-gsm3-REPLACED-REPLACED-REPL
@@ -883,6 +908,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -905,6 +931,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -935,12 +962,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -980,31 +1010,31 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 502
+size: 515
 location: src/tests/client/test-client.py:test_003()/24
 cmd: $NMCLI -g all con s con-gsm3
 lang: C
 returncode: 0
-stdout: 363 bytes
+stdout: 376 bytes
 >>>
-connection:con-gsm3:UUID-con-gsm3-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::: :0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-gsm3:UUID-con-gsm3-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::: :0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 serial:5:8:even:1:100
 gsm:no:::<hidden>:0: ::<hidden>:0:no::::auto:no:
 proxy:none:no::
 
 <<<
-size: 512
+size: 525
 location: src/tests/client/test-client.py:test_003()/25
 cmd: $NMCLI -g all con s con-gsm3
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 363 bytes
+stdout: 376 bytes
 >>>
-connection:con-gsm3:UUID-con-gsm3-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::: :0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-gsm3:UUID-con-gsm3-REPLACED-REPLACED-REPL::gsm::no:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::: :0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 serial:5:8:even:1:100
 gsm:no:::<hidden>:0: ::<hidden>:0:no::::auto:no:
 proxy:none:no::
@@ -1150,12 +1180,12 @@ UUID                                  NAME
 UUID-ethernet-REPLACED-REPLACED-REPL  ethernet 
 
 <<<
-size: 5104
+size: 5347
 location: src/tests/client/test-client.py:test_003()/37
 cmd: $NMCLI -f ALL con s ethernet
 lang: C
 returncode: 0
-stdout: 4964 bytes
+stdout: 5207 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -1176,6 +1206,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -1193,7 +1224,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -1213,6 +1244,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -1243,12 +1275,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -1267,12 +1302,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 5139
+size: 5382
 location: src/tests/client/test-client.py:test_003()/38
 cmd: $NMCLI -f ALL con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4989 bytes
+stdout: 5232 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -1293,6 +1328,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -1310,7 +1346,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -1330,6 +1366,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -1360,12 +1397,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -1404,12 +1444,12 @@ stdout: 51 bytes
 GENERAL.STATE:                          aktywowano
 
 <<<
-size: 5806
+size: 6049
 location: src/tests/client/test-client.py:test_003()/41
 cmd: $NMCLI con s ethernet
 lang: C
 returncode: 0
-stdout: 5673 bytes
+stdout: 5916 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -1430,6 +1470,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -1447,7 +1488,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -1467,6 +1508,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -1497,12 +1539,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -1534,12 +1579,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5845
+size: 6088
 location: src/tests/client/test-client.py:test_003()/42
 cmd: $NMCLI con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5702 bytes
+stdout: 5945 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -1560,6 +1605,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -1577,7 +1623,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -1597,6 +1643,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -1627,12 +1674,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -2150,12 +2200,12 @@ UUID                                  NAME
 UUID-ethernet-REPLACED-REPLACED-REPL  ethernet 
 
 <<<
-size: 5104
+size: 5347
 location: src/tests/client/test-client.py:test_003()/62
 cmd: $NMCLI -f ALL con s ethernet
 lang: C
 returncode: 0
-stdout: 4964 bytes
+stdout: 5207 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -2176,6 +2226,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -2193,7 +2244,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -2213,6 +2264,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -2243,12 +2295,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -2267,12 +2322,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 5139
+size: 5382
 location: src/tests/client/test-client.py:test_003()/63
 cmd: $NMCLI -f ALL con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4989 bytes
+stdout: 5232 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -2293,6 +2348,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -2310,7 +2366,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -2330,6 +2386,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -2360,12 +2417,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -2408,12 +2468,12 @@ GENERAL.STATE:                          aktywowano
 GENERAL.STATE:                          aktywowano
 
 <<<
-size: 6516
+size: 6759
 location: src/tests/client/test-client.py:test_003()/66
 cmd: $NMCLI con s ethernet
 lang: C
 returncode: 0
-stdout: 6383 bytes
+stdout: 6626 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -2434,6 +2494,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -2451,7 +2512,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -2471,6 +2532,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -2501,12 +2563,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -2552,12 +2617,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 6559
+size: 6802
 location: src/tests/client/test-client.py:test_003()/67
 cmd: $NMCLI con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6416 bytes
+stdout: 6659 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -2578,6 +2643,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -2595,7 +2661,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -2615,6 +2681,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -2645,12 +2712,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -3204,12 +3274,12 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 6519
+size: 6762
 location: src/tests/client/test-client.py:test_003()/84
 cmd: $NMCLI con s ethernet
 lang: C
 returncode: 0
-stdout: 6386 bytes
+stdout: 6629 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -3230,6 +3300,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -3247,7 +3318,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -3267,6 +3338,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -3297,12 +3369,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -3348,12 +3423,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 6563
+size: 6806
 location: src/tests/client/test-client.py:test_003()/85
 cmd: $NMCLI con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6420 bytes
+stdout: 6663 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -3374,6 +3449,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -3391,7 +3467,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -3411,6 +3487,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -3441,12 +3518,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -3492,12 +3572,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5849
+size: 6092
 location: src/tests/client/test-client.py:test_003()/86
 cmd: $NMCLI c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 5676 bytes
+stdout: 5919 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -3518,6 +3598,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -3535,7 +3616,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -3555,6 +3636,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -3585,12 +3667,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -3622,12 +3707,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5889
+size: 6132
 location: src/tests/client/test-client.py:test_003()/87
 cmd: $NMCLI c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5706 bytes
+stdout: 5949 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -3648,6 +3733,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -3665,7 +3751,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -3685,6 +3771,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -3715,12 +3802,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -3962,12 +4052,12 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 6531
+size: 6774
 location: src/tests/client/test-client.py:test_003()/94
 cmd: $NMCLI --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 6386 bytes
+stdout: 6629 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -3988,6 +4078,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -4005,7 +4096,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -4025,6 +4116,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -4055,12 +4147,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -4106,12 +4201,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 6575
+size: 6818
 location: src/tests/client/test-client.py:test_003()/95
 cmd: $NMCLI --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6420 bytes
+stdout: 6663 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -4132,6 +4227,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -4149,7 +4245,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -4169,6 +4265,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -4199,12 +4296,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -4250,12 +4350,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5861
+size: 6104
 location: src/tests/client/test-client.py:test_003()/96
 cmd: $NMCLI --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 5676 bytes
+stdout: 5919 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -4276,6 +4376,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -4293,7 +4394,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -4313,6 +4414,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -4343,12 +4445,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -4380,12 +4485,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5901
+size: 6144
 location: src/tests/client/test-client.py:test_003()/97
 cmd: $NMCLI --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5706 bytes
+stdout: 5949 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -4406,6 +4511,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -4423,7 +4529,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -4443,6 +4549,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -4473,12 +4580,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -4736,12 +4846,12 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 7772
+size: 8015
 location: src/tests/client/test-client.py:test_003()/104
 cmd: $NMCLI --pretty con s ethernet
 lang: C
 returncode: 0
-stdout: 7629 bytes
+stdout: 7872 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -4765,6 +4875,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -4783,7 +4894,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -4804,6 +4915,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -4835,12 +4947,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -4896,12 +5011,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 7837
+size: 8080
 location: src/tests/client/test-client.py:test_003()/105
 cmd: $NMCLI --pretty con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 7684 bytes
+stdout: 7927 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -4925,6 +5040,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -4943,7 +5059,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -4964,6 +5080,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -4995,12 +5112,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -5056,12 +5176,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6790
+size: 7033
 location: src/tests/client/test-client.py:test_003()/106
 cmd: $NMCLI --pretty c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 6607 bytes
+stdout: 6850 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -5085,6 +5205,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -5103,7 +5224,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -5124,6 +5245,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -5155,12 +5277,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -5198,12 +5323,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6843
+size: 7086
 location: src/tests/client/test-client.py:test_003()/107
 cmd: $NMCLI --pretty c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6650 bytes
+stdout: 6893 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -5227,6 +5352,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -5245,7 +5371,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -5266,6 +5392,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -5297,12 +5424,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -5590,12 +5720,12 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 7784
+size: 8027
 location: src/tests/client/test-client.py:test_003()/114
 cmd: $NMCLI --pretty --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 7629 bytes
+stdout: 7872 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -5619,6 +5749,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -5637,7 +5768,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -5658,6 +5789,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -5689,12 +5821,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -5750,12 +5885,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 7849
+size: 8092
 location: src/tests/client/test-client.py:test_003()/115
 cmd: $NMCLI --pretty --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 7684 bytes
+stdout: 7927 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -5779,6 +5914,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -5797,7 +5933,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -5818,6 +5954,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -5849,12 +5986,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -5910,12 +6050,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6802
+size: 7045
 location: src/tests/client/test-client.py:test_003()/116
 cmd: $NMCLI --pretty --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 6607 bytes
+stdout: 6850 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -5939,6 +6079,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -5957,7 +6098,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -5978,6 +6119,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -6009,12 +6151,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -6052,12 +6197,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6855
+size: 7098
 location: src/tests/client/test-client.py:test_003()/117
 cmd: $NMCLI --pretty --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6650 bytes
+stdout: 6893 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -6081,6 +6226,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -6099,7 +6245,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -6120,6 +6266,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -6151,12 +6298,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -6424,12 +6574,12 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL:gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA:802-3-ethernet
 
 <<<
-size: 3475
+size: 3615
 location: src/tests/client/test-client.py:test_003()/124
 cmd: $NMCLI --terse con s ethernet
 lang: C
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -6450,6 +6600,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -6467,7 +6618,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -6487,6 +6638,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -6517,12 +6669,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -6568,12 +6723,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3485
+size: 3625
 location: src/tests/client/test-client.py:test_003()/125
 cmd: $NMCLI --terse con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -6594,6 +6749,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -6611,7 +6767,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -6631,6 +6787,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -6661,12 +6818,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -6712,12 +6872,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3125
+size: 3265
 location: src/tests/client/test-client.py:test_003()/126
 cmd: $NMCLI --terse c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -6738,6 +6898,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -6755,7 +6916,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -6775,6 +6936,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -6805,12 +6967,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -6842,12 +7007,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3135
+size: 3275
 location: src/tests/client/test-client.py:test_003()/127
 cmd: $NMCLI --terse c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -6868,6 +7033,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -6885,7 +7051,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -6905,6 +7071,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -6935,12 +7102,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -7178,12 +7348,12 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL:gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA:802-3-ethernet
 
 <<<
-size: 3487
+size: 3627
 location: src/tests/client/test-client.py:test_003()/134
 cmd: $NMCLI --terse --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -7204,6 +7374,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -7221,7 +7392,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -7241,6 +7412,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -7271,12 +7443,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -7322,12 +7497,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3497
+size: 3637
 location: src/tests/client/test-client.py:test_003()/135
 cmd: $NMCLI --terse --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -7348,6 +7523,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -7365,7 +7541,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -7385,6 +7561,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -7415,12 +7592,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -7466,12 +7646,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3137
+size: 3277
 location: src/tests/client/test-client.py:test_003()/136
 cmd: $NMCLI --terse --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -7492,6 +7672,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -7509,7 +7690,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -7529,6 +7710,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -7559,12 +7741,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -7596,12 +7781,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3147
+size: 3287
 location: src/tests/client/test-client.py:test_003()/137
 cmd: $NMCLI --terse --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -7622,6 +7807,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -7639,7 +7825,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -7659,6 +7845,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -7689,12 +7876,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -7936,24 +8126,24 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 4373
+size: 4575
 location: src/tests/client/test-client.py:test_003()/144
 cmd: $NMCLI --mode tabular con s ethernet
 lang: C
 returncode: 0
-stdout: 4224 bytes
+stdout: 4426 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    no            --       --         
@@ -7967,24 +8157,24 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 4423
+size: 4625
 location: src/tests/client/test-client.py:test_003()/145
 cmd: $NMCLI --mode tabular con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4264 bytes
+stdout: 4466 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    nie           --       --         
@@ -7998,24 +8188,24 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deza
 
 
 <<<
-size: 3911
+size: 4113
 location: src/tests/client/test-client.py:test_003()/146
 cmd: $NMCLI --mode tabular c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 3722 bytes
+stdout: 3924 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    no            --       --         
@@ -8025,24 +8215,24 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 3959
+size: 4161
 location: src/tests/client/test-client.py:test_003()/147
 cmd: $NMCLI --mode tabular c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3760 bytes
+stdout: 3962 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    nie           --       --         
@@ -8188,24 +8378,24 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 4385
+size: 4587
 location: src/tests/client/test-client.py:test_003()/154
 cmd: $NMCLI --mode tabular --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 4224 bytes
+stdout: 4426 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    no            --       --         
@@ -8219,24 +8409,24 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 4435
+size: 4637
 location: src/tests/client/test-client.py:test_003()/155
 cmd: $NMCLI --mode tabular --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4264 bytes
+stdout: 4466 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    nie           --       --         
@@ -8250,24 +8440,24 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deza
 
 
 <<<
-size: 3923
+size: 4125
 location: src/tests/client/test-client.py:test_003()/156
 cmd: $NMCLI --mode tabular --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 3722 bytes
+stdout: 3924 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    no            --       --         
@@ -8277,24 +8467,24 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 3971
+size: 4173
 location: src/tests/client/test-client.py:test_003()/157
 cmd: $NMCLI --mode tabular --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3760 bytes
+stdout: 3962 bytes
 >>>
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 proxy  none    nie           --       --         
@@ -8456,31 +8646,31 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 7031
+size: 7334
 location: src/tests/client/test-client.py:test_003()/164
 cmd: $NMCLI --mode tabular --pretty con s ethernet
 lang: C
 returncode: 0
-stdout: 6873 bytes
+stdout: 7176 bytes
 >>>
 =========================================
   Connection profile details (ethernet)
 =========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8503,31 +8693,31 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 7161
+size: 7464
 location: src/tests/client/test-client.py:test_003()/165
 cmd: $NMCLI --mode tabular --pretty con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6993 bytes
+stdout: 7296 bytes
 >>>
 ===========================================
   Szczegóły profilu połączenia (ethernet)
 ===========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8550,31 +8740,31 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deza
 
 
 <<<
-size: 6113
+size: 6416
 location: src/tests/client/test-client.py:test_003()/166
 cmd: $NMCLI --mode tabular --pretty c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 5915 bytes
+stdout: 6218 bytes
 >>>
 =========================================
   Connection profile details (ethernet)
 =========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8589,31 +8779,31 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 6215
+size: 6518
 location: src/tests/client/test-client.py:test_003()/167
 cmd: $NMCLI --mode tabular --pretty c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6007 bytes
+stdout: 6310 bytes
 >>>
 ===========================================
   Szczegóły profilu połączenia (ethernet)
 ===========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8804,31 +8994,31 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL  gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA  ethernet 
 
 <<<
-size: 7043
+size: 7346
 location: src/tests/client/test-client.py:test_003()/174
 cmd: $NMCLI --mode tabular --pretty --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 6873 bytes
+stdout: 7176 bytes
 >>>
 =========================================
   Connection profile details (ethernet)
 =========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8851,31 +9041,31 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 7173
+size: 7476
 location: src/tests/client/test-client.py:test_003()/175
 cmd: $NMCLI --mode tabular --pretty --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6993 bytes
+stdout: 7296 bytes
 >>>
 ===========================================
   Szczegóły profilu połączenia (ethernet)
 ===========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8898,31 +9088,31 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deza
 
 
 <<<
-size: 6125
+size: 6428
 location: src/tests/client/test-client.py:test_003()/176
 cmd: $NMCLI --mode tabular --pretty --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 5915 bytes
+stdout: 6218 bytes
 >>>
 =========================================
   Connection profile details (ethernet)
 =========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      no              --           --                  --                         --                     auto  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu   s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      no              --           --                  --                         --                    auto  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -8937,31 +9127,31 @@ GENERAL  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  eth0     eth0      deac
 
 
 <<<
-size: 6227
+size: 6530
 location: src/tests/client/test-client.py:test_003()/177
 cmd: $NMCLI --mode tabular --pretty --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6007 bytes
+stdout: 6310 bytes
 >>>
 ===========================================
   Szczegóły profilu połączenia (ethernet)
 ===========================================
-name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id        uuid                                  stable-id  type            interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  ethernet  UUID-ethernet-REPLACED-REPLACED-REPL  --         802-3-ethernet  --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-blacklist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-802-3-ethernet  --    0      --      nie             --           --                  --                         --                     automatyczne  --                --            --            default      --                    -1 (default)             
+name            port  speed  duplex  auto-negotiate  mac-address  cloned-mac-address  generate-mac-address-mask  mac-address-denylist  mtu           s390-subchannels  s390-nettype  s390-options  wake-on-lan  wake-on-lan-password  accept-all-mac-addresses 
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+802-3-ethernet  --    0      --      nie             --           --                  --                         --                    automatyczne  --                --            --            default      --                    -1 (default)             
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name   method  browser-only  pac-url  pac-script 
 --------------------------------------------------
@@ -9132,66 +9322,66 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL:gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA:802-3-ethernet
 
 <<<
-size: 891
+size: 904
 location: src/tests/client/test-client.py:test_003()/184
 cmd: $NMCLI --mode tabular --terse con s ethernet
 lang: C
 returncode: 0
-stdout: 735 bytes
+stdout: 748 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth1:eth1:activated:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 <<<
-size: 901
+size: 914
 location: src/tests/client/test-client.py:test_003()/185
 cmd: $NMCLI --mode tabular --terse con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 735 bytes
+stdout: 748 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth1:eth1:activated:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 <<<
-size: 739
+size: 752
 location: src/tests/client/test-client.py:test_003()/186
 cmd: $NMCLI --mode tabular --terse c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 543 bytes
+stdout: 556 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 <<<
-size: 749
+size: 762
 location: src/tests/client/test-client.py:test_003()/187
 cmd: $NMCLI --mode tabular --terse c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 543 bytes
+stdout: 556 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
@@ -9294,66 +9484,66 @@ UUID-con-gsm3-REPLACED-REPLACED-REPL:gsm
 UUID-con-xx1-REPLACED-REPLACED-REPLA:802-3-ethernet
 
 <<<
-size: 903
+size: 916
 location: src/tests/client/test-client.py:test_003()/194
 cmd: $NMCLI --mode tabular --terse --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 735 bytes
+stdout: 748 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth1:eth1:activated:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 <<<
-size: 913
+size: 926
 location: src/tests/client/test-client.py:test_003()/195
 cmd: $NMCLI --mode tabular --terse --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 735 bytes
+stdout: 748 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth1:eth1:activated:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 <<<
-size: 751
+size: 764
 location: src/tests/client/test-client.py:test_003()/196
 cmd: $NMCLI --mode tabular --terse --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 543 bytes
+stdout: 556 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
 <<<
-size: 761
+size: 774
 location: src/tests/client/test-client.py:test_003()/197
 cmd: $NMCLI --mode tabular --terse --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 543 bytes
+stdout: 556 bytes
 >>>
-connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+connection:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL::802-3-ethernet::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
 802-3-ethernet::0::no:::::auto::::default::-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 proxy:none:no::
 GENERAL:ethernet:UUID-ethernet-REPLACED-REPLACED-REPL:eth0:eth0:deactivating:no:no::no:/org/freedesktop/NetworkManager/ActiveConnection/1:/org/freedesktop/NetworkManager/Settings/Connection/6::
 
@@ -9664,12 +9854,12 @@ UUID:                                   UUID-con-xx1-REPLACED-REPLACED-REPLA
 TYPE:                                   ethernet
 
 <<<
-size: 6537
+size: 6780
 location: src/tests/client/test-client.py:test_003()/204
 cmd: $NMCLI --mode multiline con s ethernet
 lang: C
 returncode: 0
-stdout: 6386 bytes
+stdout: 6629 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -9690,6 +9880,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -9707,7 +9898,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -9727,6 +9918,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -9757,12 +9949,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -9808,12 +10003,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 6581
+size: 6824
 location: src/tests/client/test-client.py:test_003()/205
 cmd: $NMCLI --mode multiline con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6420 bytes
+stdout: 6663 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -9834,6 +10029,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -9851,7 +10047,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -9871,6 +10067,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -9901,12 +10098,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -9952,12 +10152,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5867
+size: 6110
 location: src/tests/client/test-client.py:test_003()/206
 cmd: $NMCLI --mode multiline c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 5676 bytes
+stdout: 5919 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -9978,6 +10178,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -9995,7 +10196,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -10015,6 +10216,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -10045,12 +10247,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -10082,12 +10287,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5907
+size: 6150
 location: src/tests/client/test-client.py:test_003()/207
 cmd: $NMCLI --mode multiline c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5706 bytes
+stdout: 5949 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -10108,6 +10313,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -10125,7 +10331,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -10145,6 +10351,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -10175,12 +10382,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -10626,12 +10836,12 @@ UUID:                                   UUID-con-xx1-REPLACED-REPLACED-REPLA
 TYPE:                                   ethernet
 
 <<<
-size: 6549
+size: 6792
 location: src/tests/client/test-client.py:test_003()/214
 cmd: $NMCLI --mode multiline --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 6386 bytes
+stdout: 6629 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -10652,6 +10862,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -10669,7 +10880,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -10689,6 +10900,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -10719,12 +10931,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -10770,12 +10985,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 6593
+size: 6836
 location: src/tests/client/test-client.py:test_003()/215
 cmd: $NMCLI --mode multiline --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6420 bytes
+stdout: 6663 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -10796,6 +11011,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -10813,7 +11029,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -10833,6 +11049,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -10863,12 +11080,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -10914,12 +11134,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5879
+size: 6122
 location: src/tests/client/test-client.py:test_003()/216
 cmd: $NMCLI --mode multiline --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 5676 bytes
+stdout: 5919 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -10940,6 +11160,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -10957,7 +11178,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -10977,6 +11198,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -11007,12 +11229,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -11044,12 +11269,12 @@ GENERAL.ZONE:                           --
 GENERAL.MASTER-PATH:                    --
 
 <<<
-size: 5919
+size: 6162
 location: src/tests/client/test-client.py:test_003()/217
 cmd: $NMCLI --mode multiline --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5706 bytes
+stdout: 5949 bytes
 >>>
 connection.id:                          ethernet
 connection.uuid:                        UUID-ethernet-REPLACED-REPLACED-REPL
@@ -11070,6 +11295,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -11087,7 +11313,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -11107,6 +11333,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -11137,12 +11364,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -11626,12 +11856,12 @@ TYPE:                                   ethernet
 -------------------------------------------------------------------------------
 
 <<<
-size: 7789
+size: 8032
 location: src/tests/client/test-client.py:test_003()/224
 cmd: $NMCLI --mode multiline --pretty con s ethernet
 lang: C
 returncode: 0
-stdout: 7629 bytes
+stdout: 7872 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -11655,6 +11885,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -11673,7 +11904,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -11694,6 +11925,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -11725,12 +11957,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -11786,12 +12021,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 7854
+size: 8097
 location: src/tests/client/test-client.py:test_003()/225
 cmd: $NMCLI --mode multiline --pretty con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 7684 bytes
+stdout: 7927 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -11815,6 +12050,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -11833,7 +12069,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -11854,6 +12090,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -11885,12 +12122,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -11946,12 +12186,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6807
+size: 7050
 location: src/tests/client/test-client.py:test_003()/226
 cmd: $NMCLI --mode multiline --pretty c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 6607 bytes
+stdout: 6850 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -11975,6 +12215,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -11993,7 +12234,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -12014,6 +12255,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -12045,12 +12287,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -12088,12 +12333,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6860
+size: 7103
 location: src/tests/client/test-client.py:test_003()/227
 cmd: $NMCLI --mode multiline --pretty c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6650 bytes
+stdout: 6893 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -12117,6 +12362,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -12135,7 +12381,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -12156,6 +12402,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -12187,12 +12434,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -12706,12 +12956,12 @@ TYPE:                                   ethernet
 -------------------------------------------------------------------------------
 
 <<<
-size: 7801
+size: 8044
 location: src/tests/client/test-client.py:test_003()/234
 cmd: $NMCLI --mode multiline --pretty --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 7629 bytes
+stdout: 7872 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -12735,6 +12985,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -12753,7 +13004,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -12774,6 +13025,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -12805,12 +13057,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -12866,12 +13121,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 7866
+size: 8109
 location: src/tests/client/test-client.py:test_003()/235
 cmd: $NMCLI --mode multiline --pretty --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 7684 bytes
+stdout: 7927 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -12895,6 +13150,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -12913,7 +13169,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -12934,6 +13190,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -12965,12 +13222,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -13026,12 +13286,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6819
+size: 7062
 location: src/tests/client/test-client.py:test_003()/236
 cmd: $NMCLI --mode multiline --pretty --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 6607 bytes
+stdout: 6850 bytes
 >>>
 ===============================================================================
                      Connection profile details (ethernet)
@@ -13055,6 +13315,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -13073,7 +13334,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     auto
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -13094,6 +13355,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -13125,12 +13387,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -13168,12 +13433,12 @@ GENERAL.MASTER-PATH:                    --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6872
+size: 7115
 location: src/tests/client/test-client.py:test_003()/237
 cmd: $NMCLI --mode multiline --pretty --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6650 bytes
+stdout: 6893 bytes
 >>>
 ===============================================================================
                     Szczegóły profilu połączenia (ethernet)
@@ -13197,6 +13462,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -13215,7 +13481,7 @@ connection.wait-activation-delay:       -1
 802-3-ethernet.mac-address:             --
 802-3-ethernet.cloned-mac-address:      --
 802-3-ethernet.generate-mac-address-mask:--
-802-3-ethernet.mac-address-blacklist:   --
+802-3-ethernet.mac-address-denylist:    --
 802-3-ethernet.mtu:                     automatyczne
 802-3-ethernet.s390-subchannels:        --
 802-3-ethernet.s390-nettype:            --
@@ -13236,6 +13502,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -13267,12 +13534,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -13748,12 +14018,12 @@ UUID:UUID-con-xx1-REPLACED-REPLACED-REPLA
 TYPE:802-3-ethernet
 
 <<<
-size: 3492
+size: 3632
 location: src/tests/client/test-client.py:test_003()/244
 cmd: $NMCLI --mode multiline --terse con s ethernet
 lang: C
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -13774,6 +14044,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -13791,7 +14062,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -13811,6 +14082,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -13841,12 +14113,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -13892,12 +14167,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3502
+size: 3642
 location: src/tests/client/test-client.py:test_003()/245
 cmd: $NMCLI --mode multiline --terse con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -13918,6 +14193,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -13935,7 +14211,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -13955,6 +14231,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -13985,12 +14262,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -14036,12 +14316,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3142
+size: 3282
 location: src/tests/client/test-client.py:test_003()/246
 cmd: $NMCLI --mode multiline --terse c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -14062,6 +14342,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -14079,7 +14360,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -14099,6 +14380,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -14129,12 +14411,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -14166,12 +14451,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3152
+size: 3292
 location: src/tests/client/test-client.py:test_003()/247
 cmd: $NMCLI --mode multiline --terse c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -14192,6 +14477,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -14209,7 +14495,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -14229,6 +14515,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -14259,12 +14546,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -14710,12 +15000,12 @@ UUID:UUID-con-xx1-REPLACED-REPLACED-REPLA
 TYPE:802-3-ethernet
 
 <<<
-size: 3504
+size: 3644
 location: src/tests/client/test-client.py:test_003()/254
 cmd: $NMCLI --mode multiline --terse --color yes con s ethernet
 lang: C
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -14736,6 +15026,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -14753,7 +15044,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -14773,6 +15064,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -14803,12 +15095,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -14854,12 +15149,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3514
+size: 3654
 location: src/tests/client/test-client.py:test_003()/255
 cmd: $NMCLI --mode multiline --terse --color yes con s ethernet
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3333 bytes
+stdout: 3473 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -14880,6 +15175,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -14897,7 +15193,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -14917,6 +15213,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -14947,12 +15244,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -14998,12 +15298,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3154
+size: 3294
 location: src/tests/client/test-client.py:test_003()/256
 cmd: $NMCLI --mode multiline --terse --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: C
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -15024,6 +15324,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -15041,7 +15342,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -15061,6 +15362,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -15091,12 +15393,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -15128,12 +15433,12 @@ GENERAL.ZONE:
 GENERAL.MASTER-PATH:
 
 <<<
-size: 3164
+size: 3304
 location: src/tests/client/test-client.py:test_003()/257
 cmd: $NMCLI --mode multiline --terse --color yes c s /org/freedesktop/NetworkManager/ActiveConnection/1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2943 bytes
+stdout: 3083 bytes
 >>>
 connection.id:ethernet
 connection.uuid:UUID-ethernet-REPLACED-REPLACED-REPL
@@ -15154,6 +15459,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -15171,7 +15477,7 @@ connection.wait-activation-delay:-1
 802-3-ethernet.mac-address:
 802-3-ethernet.cloned-mac-address:
 802-3-ethernet.generate-mac-address-mask:
-802-3-ethernet.mac-address-blacklist:
+802-3-ethernet.mac-address-denylist:
 802-3-ethernet.mtu:auto
 802-3-ethernet.s390-subchannels:
 802-3-ethernet.s390-nettype:
@@ -15191,6 +15497,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -15221,12 +15528,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
diff --git a/src/tests/client/test-client.check-on-disk/test_004.expected b/src/tests/client/test-client.check-on-disk/test_004.expected
index 7e1e14b1..63e58f1f 100644
--- a/src/tests/client/test-client.check-on-disk/test_004.expected
+++ b/src/tests/client/test-client.check-on-disk/test_004.expected
@@ -48,9 +48,9 @@ stderr: 116 bytes
 Error: invalid or not allowed setting '802': '802' is ambiguous: 802-11-wireless, 802-11-wireless-security, 802-1x.
 
 <<<
-size: 135
+size: 189
 location: src/tests/client/test-client.py:test_004()/6
-cmd: $NMCLI connection mod con-xx1 802-11-wireless.band a
+cmd: $NMCLI connection mod con-xx1 802-11-wireless.band a 802-11-wireless.mac-address-denylist aA:Bb:cC:dd:EE:f
 lang: C
 returncode: 0
 size: 241
@@ -58,12 +58,12 @@ location: src/tests/client/test-client.py:test_004()/7
 cmd: $NMCLI connection mod con-xx1 ipv4.addresses 192.168.77.5/24 ipv4.routes '2.3.4.5/32 192.168.77.1' ipv6.addresses 1:2:3:4::6/64 ipv6.routes 1:2:3:4:5:6::5/128
 lang: C
 returncode: 0
-size: 5262
+size: 5578
 location: src/tests/client/test-client.py:test_004()/8
 cmd: $NMCLI con s con-xx1
 lang: C
 returncode: 0
-stdout: 5131 bytes
+stdout: 5447 bytes
 >>>
 connection.id:                          con-xx1
 connection.uuid:                        UUID-con-xx1-REPLACED-REPLACED-REPLA
@@ -84,6 +84,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -102,7 +103,8 @@ connection.wait-activation-delay:       -1
 802-11-wireless.mac-address:            --
 802-11-wireless.cloned-mac-address:     --
 802-11-wireless.generate-mac-address-mask:--
-802-11-wireless.mac-address-blacklist:  --
+802-11-wireless.mac-address-blacklist:  AA:BB:CC:DD:EE:0F
+802-11-wireless.mac-address-denylist:   AA:BB:CC:DD:EE:0F
 802-11-wireless.mac-address-randomization:default
 802-11-wireless.mtu:                    auto
 802-11-wireless.seen-bssids:            --
@@ -122,6 +124,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -152,12 +155,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -176,12 +182,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 5297
+size: 5613
 location: src/tests/client/test-client.py:test_004()/9
 cmd: $NMCLI con s con-xx1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5156 bytes
+stdout: 5472 bytes
 >>>
 connection.id:                          con-xx1
 connection.uuid:                        UUID-con-xx1-REPLACED-REPLACED-REPLA
@@ -202,6 +208,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -220,7 +227,8 @@ connection.wait-activation-delay:       -1
 802-11-wireless.mac-address:            --
 802-11-wireless.cloned-mac-address:     --
 802-11-wireless.generate-mac-address-mask:--
-802-11-wireless.mac-address-blacklist:  --
+802-11-wireless.mac-address-blacklist:  AA:BB:CC:DD:EE:0F
+802-11-wireless.mac-address-denylist:   AA:BB:CC:DD:EE:0F
 802-11-wireless.mac-address-randomization:default
 802-11-wireless.mtu:                    automatyczne
 802-11-wireless.seen-bssids:            --
@@ -240,6 +248,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -270,12 +279,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -330,12 +342,12 @@ con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  vpn       --
 con-xx1    UUID-con-xx1-REPLACED-REPLACED-REPLA  wifi      --     
 
 <<<
-size: 4760
+size: 5003
 location: src/tests/client/test-client.py:test_004()/13
 cmd: $NMCLI con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4626 bytes
+stdout: 4869 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -356,6 +368,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -378,6 +391,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -408,12 +422,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -438,12 +455,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 4787
+size: 5030
 location: src/tests/client/test-client.py:test_004()/14
 cmd: $NMCLI con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4643 bytes
+stdout: 4886 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -464,6 +481,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -486,6 +504,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -516,12 +535,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -618,12 +640,12 @@ con-xx1    UUID-con-xx1-REPLACED-REPLACED-REPLA  wifi      wlan0
 con-1      5fcfd6d7-1e63-3332-8826-a7eda103792d  ethernet  --     
 
 <<<
-size: 5888
+size: 6131
 location: src/tests/client/test-client.py:test_004()/21
 cmd: $NMCLI con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5754 bytes
+stdout: 5997 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -644,6 +666,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -666,6 +689,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -696,12 +720,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -747,12 +774,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5921
+size: 6164
 location: src/tests/client/test-client.py:test_004()/22
 cmd: $NMCLI con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5777 bytes
+stdout: 6020 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -773,6 +800,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -795,6 +823,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -825,12 +854,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -984,12 +1016,12 @@ con-xx1    UUID-con-xx1-REPLACED-REPLACED-REPLA  wifi      0          never
 con-1      5fcfd6d7-1e63-3332-8826-a7eda103792d  ethernet  0          never           yes          0                     no        /org/freedesktop/NetworkManager/Settings/Connection/1  no      --      --         --                                                  --     /etc/NetworkManager/system-connections/con-1     
 
 <<<
-size: 5894
+size: 6137
 location: src/tests/client/test-client.py:test_004()/27
 cmd: $NMCLI con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -1010,6 +1042,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -1032,6 +1065,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -1062,12 +1096,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -1113,12 +1150,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5931
+size: 6174
 location: src/tests/client/test-client.py:test_004()/28
 cmd: $NMCLI con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -1139,6 +1176,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -1161,6 +1199,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -1191,12 +1230,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -1242,12 +1284,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5894
+size: 6137
 location: src/tests/client/test-client.py:test_004()/29
 cmd: $NMCLI con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -1268,6 +1310,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -1290,6 +1333,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -1320,12 +1364,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -1371,12 +1418,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5931
+size: 6174
 location: src/tests/client/test-client.py:test_004()/30
 cmd: $NMCLI con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -1397,6 +1444,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -1419,6 +1467,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -1449,12 +1498,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -1500,12 +1552,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 4767
+size: 5010
 location: src/tests/client/test-client.py:test_004()/31
 cmd: $NMCLI -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4626 bytes
+stdout: 4869 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -1526,6 +1578,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -1548,6 +1601,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -1578,12 +1632,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -1608,12 +1665,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 4794
+size: 5037
 location: src/tests/client/test-client.py:test_004()/32
 cmd: $NMCLI -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4643 bytes
+stdout: 4886 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -1634,6 +1691,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -1656,6 +1714,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -1686,12 +1745,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -4326,12 +4388,12 @@ connection.type:                        802-11-wireless
 connection.interface-name:              --
 
 <<<
-size: 5906
+size: 6149
 location: src/tests/client/test-client.py:test_004()/77
 cmd: $NMCLI --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -4352,6 +4414,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -4374,6 +4437,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -4404,12 +4468,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -4455,12 +4522,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5943
+size: 6186
 location: src/tests/client/test-client.py:test_004()/78
 cmd: $NMCLI --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -4481,6 +4548,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -4503,6 +4571,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -4533,12 +4602,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -4584,12 +4656,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5906
+size: 6149
 location: src/tests/client/test-client.py:test_004()/79
 cmd: $NMCLI --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -4610,6 +4682,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -4632,6 +4705,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -4662,12 +4736,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -4713,12 +4790,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5943
+size: 6186
 location: src/tests/client/test-client.py:test_004()/80
 cmd: $NMCLI --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -4739,6 +4816,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -4761,6 +4839,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -4791,12 +4870,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -4842,12 +4924,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 4779
+size: 5022
 location: src/tests/client/test-client.py:test_004()/81
 cmd: $NMCLI --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4626 bytes
+stdout: 4869 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -4868,6 +4950,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -4890,6 +4973,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -4920,12 +5004,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -4950,12 +5037,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 4806
+size: 5049
 location: src/tests/client/test-client.py:test_004()/82
 cmd: $NMCLI --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4643 bytes
+stdout: 4886 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -4976,6 +5063,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -4998,6 +5086,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -5028,12 +5117,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -7668,12 +7760,12 @@ connection.type:                        802-11-wireless
 connection.interface-name:              --
 
 <<<
-size: 6915
+size: 7158
 location: src/tests/client/test-client.py:test_004()/127
 cmd: $NMCLI --pretty con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -7697,6 +7789,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -7720,6 +7813,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -7751,12 +7845,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -7810,12 +7907,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6965
+size: 7208
 location: src/tests/client/test-client.py:test_004()/128
 cmd: $NMCLI --pretty con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -7839,6 +7936,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -7862,6 +7960,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -7893,12 +7992,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -7952,12 +8054,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6915
+size: 7158
 location: src/tests/client/test-client.py:test_004()/129
 cmd: $NMCLI --pretty con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -7981,6 +8083,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -8004,6 +8107,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -8035,12 +8139,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -8094,12 +8201,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6965
+size: 7208
 location: src/tests/client/test-client.py:test_004()/130
 cmd: $NMCLI --pretty con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -8123,6 +8230,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -8146,6 +8254,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -8177,12 +8286,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -8236,12 +8348,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 5396
+size: 5639
 location: src/tests/client/test-client.py:test_004()/131
 cmd: $NMCLI --pretty -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5245 bytes
+stdout: 5488 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -8265,6 +8377,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -8288,6 +8401,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -8319,12 +8433,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -8352,12 +8469,12 @@ proxy.pac-script:                       --
 -------------------------------------------------------------------------------
 
 <<<
-size: 5428
+size: 5671
 location: src/tests/client/test-client.py:test_004()/132
 cmd: $NMCLI --pretty -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5267 bytes
+stdout: 5510 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -8381,6 +8498,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -8404,6 +8522,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -8435,12 +8554,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -11682,12 +11804,12 @@ connection.interface-name:              --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6927
+size: 7170
 location: src/tests/client/test-client.py:test_004()/177
 cmd: $NMCLI --pretty --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -11711,6 +11833,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -11734,6 +11857,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -11765,12 +11889,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -11824,12 +11951,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6977
+size: 7220
 location: src/tests/client/test-client.py:test_004()/178
 cmd: $NMCLI --pretty --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -11853,6 +11980,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -11876,6 +12004,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -11907,12 +12036,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -11966,12 +12098,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6927
+size: 7170
 location: src/tests/client/test-client.py:test_004()/179
 cmd: $NMCLI --pretty --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -11995,6 +12127,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -12018,6 +12151,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -12049,12 +12183,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -12108,12 +12245,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6977
+size: 7220
 location: src/tests/client/test-client.py:test_004()/180
 cmd: $NMCLI --pretty --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -12137,6 +12274,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -12160,6 +12298,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -12191,12 +12330,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -12250,12 +12392,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 5408
+size: 5651
 location: src/tests/client/test-client.py:test_004()/181
 cmd: $NMCLI --pretty --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5245 bytes
+stdout: 5488 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -12279,6 +12421,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -12302,6 +12445,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -12333,12 +12477,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -12366,12 +12513,12 @@ proxy.pac-script:                       --
 -------------------------------------------------------------------------------
 
 <<<
-size: 5440
+size: 5683
 location: src/tests/client/test-client.py:test_004()/182
 cmd: $NMCLI --pretty --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5267 bytes
+stdout: 5510 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -12395,6 +12542,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -12418,6 +12566,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -12449,12 +12598,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -15696,12 +15848,12 @@ connection.interface-name:              --
 -------------------------------------------------------------------------------
 
 <<<
-size: 2964
+size: 3105
 location: src/tests/client/test-client.py:test_004()/227
 cmd: $NMCLI --terse con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -15722,6 +15874,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -15744,6 +15897,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -15774,12 +15928,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -15825,12 +15982,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2974
+size: 3115
 location: src/tests/client/test-client.py:test_004()/228
 cmd: $NMCLI --terse con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -15851,6 +16008,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -15873,6 +16031,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -15903,12 +16062,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -15954,12 +16116,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2964
+size: 3105
 location: src/tests/client/test-client.py:test_004()/229
 cmd: $NMCLI --terse con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -15980,6 +16142,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -16002,6 +16165,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -16032,12 +16196,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -16083,12 +16250,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2974
+size: 3115
 location: src/tests/client/test-client.py:test_004()/230
 cmd: $NMCLI --terse con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -16109,6 +16276,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -16131,6 +16299,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -16161,12 +16330,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -16212,12 +16384,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2389
+size: 2530
 location: src/tests/client/test-client.py:test_004()/231
 cmd: $NMCLI --terse -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -16238,6 +16410,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -16260,6 +16433,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -16290,12 +16464,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -16320,12 +16497,12 @@ proxy.pac-url:
 proxy.pac-script:
 
 <<<
-size: 2399
+size: 2540
 location: src/tests/client/test-client.py:test_004()/232
 cmd: $NMCLI --terse -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -16346,6 +16523,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -16368,6 +16546,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -16398,12 +16577,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -19008,12 +19190,12 @@ connection.type:802-11-wireless
 connection.interface-name:
 
 <<<
-size: 2976
+size: 3117
 location: src/tests/client/test-client.py:test_004()/277
 cmd: $NMCLI --terse --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -19034,6 +19216,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -19056,6 +19239,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -19086,12 +19270,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -19137,12 +19324,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2986
+size: 3127
 location: src/tests/client/test-client.py:test_004()/278
 cmd: $NMCLI --terse --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -19163,6 +19350,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -19185,6 +19373,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -19215,12 +19404,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -19266,12 +19458,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2976
+size: 3117
 location: src/tests/client/test-client.py:test_004()/279
 cmd: $NMCLI --terse --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -19292,6 +19484,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -19314,6 +19507,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -19344,12 +19538,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -19395,12 +19592,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2986
+size: 3127
 location: src/tests/client/test-client.py:test_004()/280
 cmd: $NMCLI --terse --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -19421,6 +19618,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -19443,6 +19641,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -19473,12 +19672,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -19524,12 +19726,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2401
+size: 2542
 location: src/tests/client/test-client.py:test_004()/281
 cmd: $NMCLI --terse --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -19550,6 +19752,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -19572,6 +19775,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -19602,12 +19806,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -19632,12 +19839,12 @@ proxy.pac-url:
 proxy.pac-script:
 
 <<<
-size: 2411
+size: 2552
 location: src/tests/client/test-client.py:test_004()/282
 cmd: $NMCLI --terse --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -19658,6 +19865,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -19680,6 +19888,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -19710,12 +19919,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -22320,21 +22532,21 @@ connection.type:802-11-wireless
 connection.interface-name:
 
 <<<
-size: 3862
+size: 4066
 location: src/tests/client/test-client.py:test_004()/327
 cmd: $NMCLI --mode tabular con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 3712 bytes
+stdout: 3916 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  no          0       
@@ -22349,21 +22561,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3901
+size: 4105
 location: src/tests/client/test-client.py:test_004()/328
 cmd: $NMCLI --mode tabular con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3741 bytes
+stdout: 3945 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  nie         0       
@@ -22378,21 +22590,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3862
+size: 4066
 location: src/tests/client/test-client.py:test_004()/329
 cmd: $NMCLI --mode tabular con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 3712 bytes
+stdout: 3916 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  no          0       
@@ -22407,21 +22619,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3901
+size: 4105
 location: src/tests/client/test-client.py:test_004()/330
 cmd: $NMCLI --mode tabular con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3741 bytes
+stdout: 3945 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  nie         0       
@@ -22436,21 +22648,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3110
+size: 3314
 location: src/tests/client/test-client.py:test_004()/331
 cmd: $NMCLI --mode tabular -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2953 bytes
+stdout: 3157 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  no          0       
@@ -22460,21 +22672,21 @@ proxy  none    no            --       --
 
 
 <<<
-size: 3138
+size: 3342
 location: src/tests/client/test-client.py:test_004()/332
 cmd: $NMCLI --mode tabular -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2971 bytes
+stdout: 3175 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  nie         0       
@@ -23974,21 +24186,21 @@ interface-name
 
 
 <<<
-size: 3874
+size: 4078
 location: src/tests/client/test-client.py:test_004()/377
 cmd: $NMCLI --mode tabular --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 3712 bytes
+stdout: 3916 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  no          0       
@@ -24003,21 +24215,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3913
+size: 4117
 location: src/tests/client/test-client.py:test_004()/378
 cmd: $NMCLI --mode tabular --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3741 bytes
+stdout: 3945 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  nie         0       
@@ -24032,21 +24244,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3874
+size: 4078
 location: src/tests/client/test-client.py:test_004()/379
 cmd: $NMCLI --mode tabular --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 3712 bytes
+stdout: 3916 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  no          0       
@@ -24061,21 +24273,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3913
+size: 4117
 location: src/tests/client/test-client.py:test_004()/380
 cmd: $NMCLI --mode tabular --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 3741 bytes
+stdout: 3945 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  nie         0       
@@ -24090,21 +24302,21 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 3122
+size: 3326
 location: src/tests/client/test-client.py:test_004()/381
 cmd: $NMCLI --mode tabular --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2953 bytes
+stdout: 3157 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  no          0       
@@ -24114,21 +24326,21 @@ proxy  none    no            --       --
 
 
 <<<
-size: 3150
+size: 3354
 location: src/tests/client/test-client.py:test_004()/382
 cmd: $NMCLI --mode tabular --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2971 bytes
+stdout: 3175 bytes
 >>>
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 vpn   org.freedesktop.NetworkManager.openvpn  --         key1 = val1, key2 = val2, key3 = val3  <hidden>  nie         0       
@@ -25628,27 +25840,27 @@ interface-name
 
 
 <<<
-size: 6063
+size: 6369
 location: src/tests/client/test-client.py:test_004()/427
 cmd: $NMCLI --mode tabular --pretty con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5904 bytes
+stdout: 6210 bytes
 >>>
 ==========================================
   Connection profile details (con-vpn-1)
 ==========================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -25670,27 +25882,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 6149
+size: 6455
 location: src/tests/client/test-client.py:test_004()/428
 cmd: $NMCLI --mode tabular --pretty con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5980 bytes
+stdout: 6286 bytes
 >>>
 ============================================
   Szczegóły profilu połączenia (con-vpn-1)
 ============================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -25712,27 +25924,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 6063
+size: 6369
 location: src/tests/client/test-client.py:test_004()/429
 cmd: $NMCLI --mode tabular --pretty con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5904 bytes
+stdout: 6210 bytes
 >>>
 ==========================================
   Connection profile details (con-vpn-1)
 ==========================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -25754,27 +25966,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 6149
+size: 6455
 location: src/tests/client/test-client.py:test_004()/430
 cmd: $NMCLI --mode tabular --pretty con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5980 bytes
+stdout: 6286 bytes
 >>>
 ============================================
   Szczegóły profilu połączenia (con-vpn-1)
 ============================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -25796,27 +26008,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 4725
+size: 5031
 location: src/tests/client/test-client.py:test_004()/431
 cmd: $NMCLI --mode tabular --pretty -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4559 bytes
+stdout: 4865 bytes
 >>>
 ==========================================
   Connection profile details (con-vpn-1)
 ==========================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -25828,27 +26040,27 @@ proxy  none    no            --       --
 
 
 <<<
-size: 4772
+size: 5078
 location: src/tests/client/test-client.py:test_004()/432
 cmd: $NMCLI --mode tabular --pretty -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4596 bytes
+stdout: 4902 bytes
 >>>
 ============================================
   Szczegóły profilu połączenia (con-vpn-1)
 ============================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -27936,27 +28148,27 @@ interface-name
 
 
 <<<
-size: 6075
+size: 6381
 location: src/tests/client/test-client.py:test_004()/477
 cmd: $NMCLI --mode tabular --pretty --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5904 bytes
+stdout: 6210 bytes
 >>>
 ==========================================
   Connection profile details (con-vpn-1)
 ==========================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -27978,27 +28190,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 6161
+size: 6467
 location: src/tests/client/test-client.py:test_004()/478
 cmd: $NMCLI --mode tabular --pretty --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5980 bytes
+stdout: 6286 bytes
 >>>
 ============================================
   Szczegóły profilu połączenia (con-vpn-1)
 ============================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -28020,27 +28232,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 6075
+size: 6381
 location: src/tests/client/test-client.py:test_004()/479
 cmd: $NMCLI --mode tabular --pretty --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5904 bytes
+stdout: 6210 bytes
 >>>
 ==========================================
   Connection profile details (con-vpn-1)
 ==========================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -28062,27 +28274,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 - VPN connected  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 6161
+size: 6467
 location: src/tests/client/test-client.py:test_004()/480
 cmd: $NMCLI --mode tabular --pretty --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5980 bytes
+stdout: 6286 bytes
 >>>
 ============================================
   Szczegóły profilu połączenia (con-vpn-1)
 ============================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -28104,27 +28316,27 @@ NAME  TYPE     USERNAME  GATEWAY  BANNER                            VPN-STATE
 VPN   openvpn  --        --       *** VPN connection con-vpn-1 ***  5 — Połączono z VPN  key1 = val1 | key2 = val2 | key3 = val3 
 
 <<<
-size: 4737
+size: 5043
 location: src/tests/client/test-client.py:test_004()/481
 cmd: $NMCLI --mode tabular --pretty --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4559 bytes
+stdout: 4865 bytes
 >>>
 ==========================================
   Connection profile details (con-vpn-1)
 ==========================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered  lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              yes          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     unknown  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               --              --         --         0 (default)   yes                 --             --         0x0 (none)           no             yes       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        no                  no               no             yes       -1 (default)      -1 (unknown)  default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu   dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       no                  no               no             yes       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  auto  --            --         --         0 (default)   yes                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -28136,27 +28348,27 @@ proxy  none    no            --       --
 
 
 <<<
-size: 4784
+size: 5090
 location: src/tests/client/test-client.py:test_004()/482
 cmd: $NMCLI --mode tabular --pretty --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4596 bytes
+stdout: 4902 bytes
 >>>
 ============================================
   Szczegóły profilu połączenia (con-vpn-1)
 ============================================
-name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
+name        id         uuid                                  stable-id  type  interface-name  autoconnect  autoconnect-priority  autoconnect-retries  multi-connect  auth-retries  timestamp  permissions  zone  controller  master  slave-type  port-type  autoconnect-slaves  autoconnect-ports  down-on-poweroff  secondaries  gateway-ping-timeout  metered   lldp     mdns          llmnr         dns-over-tls  mptcp-flags    wait-device-timeout  wait-activation-delay 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+connection  con-vpn-1  UUID-con-vpn-1-REPLACED-REPLACED-REP  --         vpn   --              tak          0                     -1 (default)         0 (default)    -1            0          --           --    --          --      --          --         -1 (default)        -1 (default)       -1 (default)      --           0                     nieznane  default  -1 (default)  -1 (default)  -1 (default)  0x0 (default)  -1                   -1                    
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  dhcp-client-id  dhcp-iaid  dhcp-dscp  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-fqdn  dhcp-hostname-flags  never-default  may-fail  required-timeout  dad-timeout   dhcp-vendor-class-identifier  link-local   dhcp-reject-servers  auto-route-ext-gw 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv4  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              --              --         --         0 (default)   tak                 --             --         0x0 (none)           nie            tak       -1 (default)      -1 (default)  --                            0 (default)  --                   -1 (default)      
 
-name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        nie                 nie              nie            tak       -1 (default)      -1 (unknown)  default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
+name  method  dns  dns-search  dns-options  dns-priority  addresses  gateway  routes  route-metric  route-table  routing-rules  replace-local-rule  dhcp-send-release  ignore-auto-routes  ignore-auto-dns  never-default  may-fail  required-timeout  ip6-privacy   temp-valid-lifetime  temp-preferred-lifetime  addr-gen-mode  ra-timeout   mtu           dhcp-pd-hint  dhcp-duid  dhcp-iaid  dhcp-timeout  dhcp-send-hostname  dhcp-hostname  dhcp-hostname-flags  auto-route-ext-gw  token 
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ipv6  auto    --   --          --           0             --         --       --      -1            0 (unspec)   --             -1 (default)        -1 (default)       nie                 nie              nie            tak       -1 (default)      -1 (unknown)  0                    0                        default        0 (default)  automatyczne  --            --         --         0 (default)   tak                 --             0x0 (none)           -1 (default)       --    
 
 name  service-type                            user-name  data                                   secrets   persistent  timeout 
 -------------------------------------------------------------------------------------------------------------------------------
@@ -30244,94 +30456,94 @@ interface-name
 
 
 <<<
-size: 846
+size: 859
 location: src/tests/client/test-client.py:test_004()/527
 cmd: $NMCLI --mode tabular --terse con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 856
+size: 869
 location: src/tests/client/test-client.py:test_004()/528
 cmd: $NMCLI --mode tabular --terse con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 846
+size: 859
 location: src/tests/client/test-client.py:test_004()/529
 cmd: $NMCLI --mode tabular --terse con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 856
+size: 869
 location: src/tests/client/test-client.py:test_004()/530
 cmd: $NMCLI --mode tabular --terse con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 553
+size: 566
 location: src/tests/client/test-client.py:test_004()/531
 cmd: $NMCLI --mode tabular --terse -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 389 bytes
+stdout: 402 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 
 <<<
-size: 563
+size: 576
 location: src/tests/client/test-client.py:test_004()/532
 cmd: $NMCLI --mode tabular --terse -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 389 bytes
+stdout: 402 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 
@@ -31182,94 +31394,94 @@ UUID-con-xx1-REPLACED-REPLACED-REPLA
 
 
 <<<
-size: 858
+size: 871
 location: src/tests/client/test-client.py:test_004()/577
 cmd: $NMCLI --mode tabular --terse --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 868
+size: 881
 location: src/tests/client/test-client.py:test_004()/578
 cmd: $NMCLI --mode tabular --terse --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 858
+size: 871
 location: src/tests/client/test-client.py:test_004()/579
 cmd: $NMCLI --mode tabular --terse --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 868
+size: 881
 location: src/tests/client/test-client.py:test_004()/580
 cmd: $NMCLI --mode tabular --terse --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 689 bytes
+stdout: 702 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 GENERAL:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP:wlan0:wlan0:activated:no:no::yes:/org/freedesktop/NetworkManager/ActiveConnection/2:/org/freedesktop/NetworkManager/Settings/Connection/3::
 VPN:openvpn:::*** VPN connection con-vpn-1 ***:5 - VPN connected:key1 = val1 | key2 = val2 | key3 = val3
 
 <<<
-size: 565
+size: 578
 location: src/tests/client/test-client.py:test_004()/581
 cmd: $NMCLI --mode tabular --terse --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 389 bytes
+stdout: 402 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 
 <<<
-size: 575
+size: 588
 location: src/tests/client/test-client.py:test_004()/582
 cmd: $NMCLI --mode tabular --terse --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 389 bytes
+stdout: 402 bytes
 >>>
-connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
-ipv4:auto::::0::::-1:0::-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
-ipv6:auto::::0::::-1:0::-1:no:no:no:yes:-1:-1:default:0:auto::::0:yes::0x0:-1:
+connection:con-vpn-1:UUID-con-vpn-1-REPLACED-REPLACED-REP::vpn::yes:0:-1:0:-1:0:::::::-1:-1:-1::0:unknown:default:-1:-1:-1:0x0:-1:-1
+ipv4:auto::::0::::-1:0::-1:-1:no:no::::0:yes:::0x0:no:yes:-1:-1::0::-1
+ipv6:auto::::0::::-1:0::-1:-1:no:no:no:yes:-1:-1:0:0:default:0:auto::::0:yes::0x0:-1:
 vpn:org.freedesktop.NetworkManager.openvpn::key1 = val1, key2 = val2, key3 = val3:<hidden>:no:0
 proxy:none:no::
 
@@ -32120,12 +32332,12 @@ UUID-con-xx1-REPLACED-REPLACED-REPLA
 
 
 <<<
-size: 5912
+size: 6155
 location: src/tests/client/test-client.py:test_004()/627
 cmd: $NMCLI --mode multiline con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -32146,6 +32358,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -32168,6 +32381,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -32198,12 +32412,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -32249,12 +32466,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5949
+size: 6192
 location: src/tests/client/test-client.py:test_004()/628
 cmd: $NMCLI --mode multiline con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -32275,6 +32492,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -32297,6 +32515,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -32327,12 +32546,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -32378,12 +32600,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5912
+size: 6155
 location: src/tests/client/test-client.py:test_004()/629
 cmd: $NMCLI --mode multiline con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -32404,6 +32626,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -32426,6 +32649,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -32456,12 +32680,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -32507,12 +32734,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5949
+size: 6192
 location: src/tests/client/test-client.py:test_004()/630
 cmd: $NMCLI --mode multiline con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -32533,6 +32760,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -32555,6 +32783,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -32585,12 +32814,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -32636,12 +32868,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 4785
+size: 5028
 location: src/tests/client/test-client.py:test_004()/631
 cmd: $NMCLI --mode multiline -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4626 bytes
+stdout: 4869 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -32662,6 +32894,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -32684,6 +32917,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -32714,12 +32948,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -32744,12 +32981,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 4812
+size: 5055
 location: src/tests/client/test-client.py:test_004()/632
 cmd: $NMCLI --mode multiline -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4643 bytes
+stdout: 4886 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -32770,6 +33007,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -32792,6 +33030,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -32822,12 +33061,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -35972,12 +36214,12 @@ connection.type:                        802-11-wireless
 connection.interface-name:              --
 
 <<<
-size: 5924
+size: 6167
 location: src/tests/client/test-client.py:test_004()/677
 cmd: $NMCLI --mode multiline --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -35998,6 +36240,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -36020,6 +36263,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -36050,12 +36294,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -36101,12 +36348,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5961
+size: 6204
 location: src/tests/client/test-client.py:test_004()/678
 cmd: $NMCLI --mode multiline --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -36127,6 +36374,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -36149,6 +36397,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -36179,12 +36428,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -36230,12 +36482,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5924
+size: 6167
 location: src/tests/client/test-client.py:test_004()/679
 cmd: $NMCLI --mode multiline --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5760 bytes
+stdout: 6003 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -36256,6 +36508,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -36278,6 +36531,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -36308,12 +36562,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -36359,12 +36616,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 5961
+size: 6204
 location: src/tests/client/test-client.py:test_004()/680
 cmd: $NMCLI --mode multiline --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5787 bytes
+stdout: 6030 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -36385,6 +36642,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -36407,6 +36665,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -36437,12 +36696,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -36488,12 +36750,12 @@ VPN.CFG[2]:                             key2 = val2
 VPN.CFG[3]:                             key3 = val3
 
 <<<
-size: 4797
+size: 5040
 location: src/tests/client/test-client.py:test_004()/681
 cmd: $NMCLI --mode multiline --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 4626 bytes
+stdout: 4869 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -36514,6 +36776,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -36536,6 +36799,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -36566,12 +36830,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -36596,12 +36863,12 @@ proxy.pac-url:                          --
 proxy.pac-script:                       --
 
 <<<
-size: 4824
+size: 5067
 location: src/tests/client/test-client.py:test_004()/682
 cmd: $NMCLI --mode multiline --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 4643 bytes
+stdout: 4886 bytes
 >>>
 connection.id:                          con-vpn-1
 connection.uuid:                        UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -36622,6 +36889,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -36644,6 +36912,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -36674,12 +36943,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -39824,12 +40096,12 @@ connection.type:                        802-11-wireless
 connection.interface-name:              --
 
 <<<
-size: 6932
+size: 7175
 location: src/tests/client/test-client.py:test_004()/727
 cmd: $NMCLI --mode multiline --pretty con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -39853,6 +40125,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -39876,6 +40149,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -39907,12 +40181,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -39966,12 +40243,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6982
+size: 7225
 location: src/tests/client/test-client.py:test_004()/728
 cmd: $NMCLI --mode multiline --pretty con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -39995,6 +40272,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -40018,6 +40296,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -40049,12 +40328,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -40108,12 +40390,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6932
+size: 7175
 location: src/tests/client/test-client.py:test_004()/729
 cmd: $NMCLI --mode multiline --pretty con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -40137,6 +40419,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -40160,6 +40443,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -40191,12 +40475,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -40250,12 +40537,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6982
+size: 7225
 location: src/tests/client/test-client.py:test_004()/730
 cmd: $NMCLI --mode multiline --pretty con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -40279,6 +40566,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -40302,6 +40590,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -40333,12 +40622,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -40392,12 +40684,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 5413
+size: 5656
 location: src/tests/client/test-client.py:test_004()/731
 cmd: $NMCLI --mode multiline --pretty -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5245 bytes
+stdout: 5488 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -40421,6 +40713,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -40444,6 +40737,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -40475,12 +40769,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -40508,12 +40805,12 @@ proxy.pac-script:                       --
 -------------------------------------------------------------------------------
 
 <<<
-size: 5445
+size: 5688
 location: src/tests/client/test-client.py:test_004()/732
 cmd: $NMCLI --mode multiline --pretty -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5267 bytes
+stdout: 5510 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -40537,6 +40834,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -40560,6 +40858,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -40591,12 +40890,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -44378,12 +44680,12 @@ connection.interface-name:              --
 -------------------------------------------------------------------------------
 
 <<<
-size: 6944
+size: 7187
 location: src/tests/client/test-client.py:test_004()/777
 cmd: $NMCLI --mode multiline --pretty --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -44407,6 +44709,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -44430,6 +44733,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -44461,12 +44765,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -44520,12 +44827,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6994
+size: 7237
 location: src/tests/client/test-client.py:test_004()/778
 cmd: $NMCLI --mode multiline --pretty --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -44549,6 +44856,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -44572,6 +44880,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -44603,12 +44912,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -44662,12 +44974,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6944
+size: 7187
 location: src/tests/client/test-client.py:test_004()/779
 cmd: $NMCLI --mode multiline --pretty --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 6771 bytes
+stdout: 7014 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -44691,6 +45003,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -44714,6 +45027,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -44745,12 +45059,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -44804,12 +45121,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 6994
+size: 7237
 location: src/tests/client/test-client.py:test_004()/780
 cmd: $NMCLI --mode multiline --pretty --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 6811 bytes
+stdout: 7054 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -44833,6 +45150,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -44856,6 +45174,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -44887,12 +45206,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -44946,12 +45268,12 @@ VPN.CFG[3]:                             key3 = val3
 -------------------------------------------------------------------------------
 
 <<<
-size: 5425
+size: 5668
 location: src/tests/client/test-client.py:test_004()/781
 cmd: $NMCLI --mode multiline --pretty --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 5245 bytes
+stdout: 5488 bytes
 >>>
 ===============================================================================
                     Connection profile details (con-vpn-1)
@@ -44975,6 +45297,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     unknown
@@ -44998,6 +45321,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                no
 ipv4.ignore-auto-dns:                   no
 ipv4.dhcp-client-id:                    --
@@ -45029,12 +45353,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                no
 ipv6.ignore-auto-dns:                   no
 ipv6.never-default:                     no
 ipv6.may-fail:                          yes
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               auto
@@ -45062,12 +45389,12 @@ proxy.pac-script:                       --
 -------------------------------------------------------------------------------
 
 <<<
-size: 5457
+size: 5700
 location: src/tests/client/test-client.py:test_004()/782
 cmd: $NMCLI --mode multiline --pretty --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 5267 bytes
+stdout: 5510 bytes
 >>>
 ===============================================================================
                    Szczegóły profilu połączenia (con-vpn-1)
@@ -45091,6 +45418,7 @@ connection.slave-type:                  --
 connection.port-type:                   --
 connection.autoconnect-slaves:          -1 (default)
 connection.autoconnect-ports:           -1 (default)
+connection.down-on-poweroff:            -1 (default)
 connection.secondaries:                 --
 connection.gateway-ping-timeout:        0
 connection.metered:                     nieznane
@@ -45114,6 +45442,7 @@ ipv4.route-metric:                      -1
 ipv4.route-table:                       0 (unspec)
 ipv4.routing-rules:                     --
 ipv4.replace-local-rule:                -1 (default)
+ipv4.dhcp-send-release:                 -1 (default)
 ipv4.ignore-auto-routes:                nie
 ipv4.ignore-auto-dns:                   nie
 ipv4.dhcp-client-id:                    --
@@ -45145,12 +45474,15 @@ ipv6.route-metric:                      -1
 ipv6.route-table:                       0 (unspec)
 ipv6.routing-rules:                     --
 ipv6.replace-local-rule:                -1 (default)
+ipv6.dhcp-send-release:                 -1 (default)
 ipv6.ignore-auto-routes:                nie
 ipv6.ignore-auto-dns:                   nie
 ipv6.never-default:                     nie
 ipv6.may-fail:                          tak
 ipv6.required-timeout:                  -1 (default)
 ipv6.ip6-privacy:                       -1 (unknown)
+ipv6.temp-valid-lifetime:               0
+ipv6.temp-preferred-lifetime:           0
 ipv6.addr-gen-mode:                     default
 ipv6.ra-timeout:                        0 (default)
 ipv6.mtu:                               automatyczne
@@ -48932,12 +49264,12 @@ connection.interface-name:              --
 -------------------------------------------------------------------------------
 
 <<<
-size: 2981
+size: 3122
 location: src/tests/client/test-client.py:test_004()/827
 cmd: $NMCLI --mode multiline --terse con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -48958,6 +49290,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -48980,6 +49313,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -49010,12 +49344,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -49061,12 +49398,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2991
+size: 3132
 location: src/tests/client/test-client.py:test_004()/828
 cmd: $NMCLI --mode multiline --terse con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -49087,6 +49424,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -49109,6 +49447,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -49139,12 +49478,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -49190,12 +49532,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2981
+size: 3122
 location: src/tests/client/test-client.py:test_004()/829
 cmd: $NMCLI --mode multiline --terse con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -49216,6 +49558,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -49238,6 +49581,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -49268,12 +49612,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -49319,12 +49666,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2991
+size: 3132
 location: src/tests/client/test-client.py:test_004()/830
 cmd: $NMCLI --mode multiline --terse con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -49345,6 +49692,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -49367,6 +49715,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -49397,12 +49746,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -49448,12 +49800,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2406
+size: 2547
 location: src/tests/client/test-client.py:test_004()/831
 cmd: $NMCLI --mode multiline --terse -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -49474,6 +49826,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -49496,6 +49849,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -49526,12 +49880,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -49556,12 +49913,12 @@ proxy.pac-url:
 proxy.pac-script:
 
 <<<
-size: 2416
+size: 2557
 location: src/tests/client/test-client.py:test_004()/832
 cmd: $NMCLI --mode multiline --terse -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -49582,6 +49939,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -49604,6 +49962,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -49634,12 +49993,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -52784,12 +53146,12 @@ connection.type:802-11-wireless
 connection.interface-name:
 
 <<<
-size: 2993
+size: 3134
 location: src/tests/client/test-client.py:test_004()/877
 cmd: $NMCLI --mode multiline --terse --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -52810,6 +53172,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -52832,6 +53195,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -52862,12 +53226,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -52913,12 +53280,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 3003
+size: 3144
 location: src/tests/client/test-client.py:test_004()/878
 cmd: $NMCLI --mode multiline --terse --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -52939,6 +53306,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -52961,6 +53329,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -52991,12 +53360,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -53042,12 +53414,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2993
+size: 3134
 location: src/tests/client/test-client.py:test_004()/879
 cmd: $NMCLI --mode multiline --terse --color yes con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -53068,6 +53440,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -53090,6 +53463,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -53120,12 +53494,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -53171,12 +53548,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 3003
+size: 3144
 location: src/tests/client/test-client.py:test_004()/880
 cmd: $NMCLI --mode multiline --terse --color yes con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2821 bytes
+stdout: 2962 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -53197,6 +53574,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -53219,6 +53597,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -53249,12 +53628,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -53300,12 +53682,12 @@ VPN.CFG[2]:key2 = val2
 VPN.CFG[3]:key3 = val3
 
 <<<
-size: 2418
+size: 2559
 location: src/tests/client/test-client.py:test_004()/881
 cmd: $NMCLI --mode multiline --terse --color yes -f ALL con s con-vpn-1
 lang: C
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -53326,6 +53708,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -53348,6 +53731,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -53378,12 +53762,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
@@ -53408,12 +53795,12 @@ proxy.pac-url:
 proxy.pac-script:
 
 <<<
-size: 2428
+size: 2569
 location: src/tests/client/test-client.py:test_004()/882
 cmd: $NMCLI --mode multiline --terse --color yes -f ALL con s con-vpn-1
 lang: pl_PL.UTF-8
 returncode: 0
-stdout: 2239 bytes
+stdout: 2380 bytes
 >>>
 connection.id:con-vpn-1
 connection.uuid:UUID-con-vpn-1-REPLACED-REPLACED-REP
@@ -53434,6 +53821,7 @@ connection.slave-type:
 connection.port-type:
 connection.autoconnect-slaves:-1
 connection.autoconnect-ports:-1
+connection.down-on-poweroff:-1
 connection.secondaries:
 connection.gateway-ping-timeout:0
 connection.metered:unknown
@@ -53456,6 +53844,7 @@ ipv4.route-metric:-1
 ipv4.route-table:0
 ipv4.routing-rules:
 ipv4.replace-local-rule:-1
+ipv4.dhcp-send-release:-1
 ipv4.ignore-auto-routes:no
 ipv4.ignore-auto-dns:no
 ipv4.dhcp-client-id:
@@ -53486,12 +53875,15 @@ ipv6.route-metric:-1
 ipv6.route-table:0
 ipv6.routing-rules:
 ipv6.replace-local-rule:-1
+ipv6.dhcp-send-release:-1
 ipv6.ignore-auto-routes:no
 ipv6.ignore-auto-dns:no
 ipv6.never-default:no
 ipv6.may-fail:yes
 ipv6.required-timeout:-1
 ipv6.ip6-privacy:-1
+ipv6.temp-valid-lifetime:0
+ipv6.temp-preferred-lifetime:0
 ipv6.addr-gen-mode:default
 ipv6.ra-timeout:0
 ipv6.mtu:auto
diff --git a/src/tests/client/test-client.py b/src/tests/client/test-client.py
index df137ee0..60853e6d 100755
--- a/src/tests/client/test-client.py
+++ b/src/tests/client/test-client.py
@@ -1810,7 +1810,17 @@ class TestNmcli(unittest.TestCase):
         )
         self.call_nmcli(["connection", "mod", "con-xx1", "ipv6.gateway", "::99"])
         self.call_nmcli(["connection", "mod", "con-xx1", "802.abc", ""])
-        self.call_nmcli(["connection", "mod", "con-xx1", "802-11-wireless.band", "a"])
+        self.call_nmcli(
+            [
+                "connection",
+                "mod",
+                "con-xx1",
+                "802-11-wireless.band",
+                "a",
+                "802-11-wireless.mac-address-denylist",
+                "aA:Bb:cC:dd:EE:f",
+            ]
+        )
         self.call_nmcli(
             [
                 "connection",