summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am30
-rw-r--r--src/Makefile.in200
-rw-r--r--src/NetworkManager.ver9
-rw-r--r--src/NetworkManagerUtils.c10
-rw-r--r--src/NetworkManagerUtils.h3
-rw-r--r--src/devices/adsl/nm-atm-manager.c2
-rw-r--r--src/devices/adsl/nm-device-adsl.c5
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c5
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c26
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c2
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c4
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h2
-rw-r--r--src/devices/bluetooth/nm-device-bt.c14
-rw-r--r--src/devices/nm-device-bond.c3
-rw-r--r--src/devices/nm-device-bridge.c3
-rw-r--r--src/devices/nm-device-ethernet.c26
-rw-r--r--src/devices/nm-device-factory.c4
-rw-r--r--src/devices/nm-device-infiniband.c5
-rw-r--r--src/devices/nm-device-ip-tunnel.c10
-rw-r--r--src/devices/nm-device-logging.h4
-rw-r--r--src/devices/nm-device-macvlan.c3
-rw-r--r--src/devices/nm-device-tun.c3
-rw-r--r--src/devices/nm-device-vlan.c3
-rw-r--r--src/devices/nm-device-vxlan.c3
-rw-r--r--src/devices/nm-device.c141
-rw-r--r--src/devices/nm-device.h5
-rw-r--r--src/devices/nm-lldp-listener.c910
-rw-r--r--src/devices/nm-lldp-listener.h3
-rw-r--r--src/devices/team/nm-device-team.c3
-rw-r--r--src/devices/tests/test-arping.c6
-rw-r--r--src/devices/tests/test-lldp.c466
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c3
-rw-r--r--src/devices/wifi/nm-device-wifi.c7
-rw-r--r--src/devices/wifi/nm-wifi-ap.c2
-rw-r--r--src/devices/wifi/tests/test-wifi-ap-utils.c2
-rw-r--r--src/devices/wwan/nm-device-modem.c7
-rw-r--r--src/devices/wwan/nm-modem-broadband.c16
-rw-r--r--src/devices/wwan/nm-modem-manager.c2
-rw-r--r--src/devices/wwan/nm-modem.c5
-rw-r--r--src/dhcp-manager/nm-dhcp-client-logging.h55
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c86
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c118
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c21
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c63
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c206
-rw-r--r--src/dhcp-manager/tests/test-dhcp-utils.c14
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.c47
-rw-r--r--src/dns-manager/nm-dns-manager.c238
-rw-r--r--src/dns-manager/nm-dns-manager.h10
-rw-r--r--src/dns-manager/nm-dns-plugin.c120
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.c28
-rw-r--r--src/main-utils.c28
-rw-r--r--src/main-utils.h1
-rw-r--r--src/main.c32
-rw-r--r--src/nm-activation-request.c5
-rw-r--r--src/nm-active-connection.c78
-rw-r--r--src/nm-active-connection.h6
-rw-r--r--src/nm-audit-manager.c22
-rw-r--r--src/nm-auth-utils.c4
-rw-r--r--src/nm-bus-manager.c63
-rw-r--r--src/nm-core-utils.c156
-rw-r--r--src/nm-core-utils.h63
-rw-r--r--src/nm-default-route-manager.c61
-rw-r--r--src/nm-default-route-manager.h3
-rw-r--r--src/nm-dispatcher.c83
-rw-r--r--src/nm-enum-types.c29
-rw-r--r--src/nm-enum-types.h2
-rw-r--r--src/nm-iface-helper.c42
-rw-r--r--src/nm-ip4-config.c4
-rw-r--r--src/nm-logging.c28
-rw-r--r--src/nm-logging.h21
-rw-r--r--src/nm-manager.c566
-rw-r--r--src/nm-policy.c820
-rw-r--r--src/nm-policy.h6
-rw-r--r--src/nm-route-manager.c48
-rw-r--r--src/nm-route-manager.h3
-rw-r--r--src/nm-session-monitor.c16
-rw-r--r--src/nm-types.h3
-rw-r--r--src/org.freedesktop.NetworkManager.conf19
-rw-r--r--src/platform/nm-fake-platform.c2
-rw-r--r--src/platform/nm-linux-platform.c282
-rw-r--r--src/platform/nm-platform-utils.c106
-rw-r--r--src/platform/nm-platform-utils.h15
-rw-r--r--src/platform/nm-platform.c199
-rw-r--r--src/platform/nm-platform.h25
-rw-r--r--src/platform/nmp-netns.c732
-rw-r--r--src/platform/nmp-netns.h74
-rw-r--r--src/platform/nmp-object.c27
-rw-r--r--src/platform/nmp-object.h4
-rw-r--r--src/platform/tests/test-address.c14
-rw-r--r--src/platform/tests/test-cleanup.c2
-rw-r--r--src/platform/tests/test-common.c122
-rw-r--r--src/platform/tests/test-common.h19
-rw-r--r--src/platform/tests/test-general.c4
-rw-r--r--src/platform/tests/test-link.c501
-rw-r--r--src/platform/tests/test-nmp-object.c4
-rw-r--r--src/platform/tests/test-route.c4
-rw-r--r--src/platform/wifi/wifi-utils-wext.c25
-rw-r--r--src/ppp-manager/nm-ppp-manager.c50
-rw-r--r--src/ppp-manager/nm-pppd-plugin.c5
-rw-r--r--src/rdisc/nm-lndp-rdisc.c30
-rw-r--r--src/rdisc/nm-lndp-rdisc.h3
-rw-r--r--src/rdisc/nm-rdisc.c107
-rw-r--r--src/rdisc/nm-rdisc.h10
-rw-r--r--src/rdisc/tests/test-rdisc-fake.c2
-rw-r--r--src/rdisc/tests/test-rdisc-linux.c3
-rw-r--r--src/settings/nm-agent-manager.c9
-rw-r--r--src/settings/nm-settings-connection.c28
-rw-r--r--src/settings/nm-settings-plugin.c2
-rw-r--r--src/settings/nm-settings.c156
-rw-r--r--src/settings/nm-settings.h7
-rw-r--r--src/settings/plugins/ibft/tests/test-ibft.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml20
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c64
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c56
-rw-r--r--src/settings/plugins/ifcfg-rh/utils.h2
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c7
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c166
-rw-r--r--src/settings/plugins/ifnet/net_parser.c64
-rw-r--r--src/settings/plugins/ifnet/net_utils.c17
-rw-r--r--src/settings/plugins/ifnet/nm-ifnet-connection.c15
-rw-r--r--src/settings/plugins/ifnet/tests/test-ifnet.c18
-rw-r--r--src/settings/plugins/ifnet/wpa_parser.c45
-rw-r--r--src/settings/plugins/ifupdown/interface_parser.c49
-rw-r--r--src/settings/plugins/ifupdown/interface_parser.h2
-rw-r--r--src/settings/plugins/ifupdown/nm-ifupdown-connection.c117
-rw-r--r--src/settings/plugins/ifupdown/nm-ifupdown-connection.h4
-rw-r--r--src/settings/plugins/ifupdown/parser.c41
-rw-r--r--src/settings/plugins/ifupdown/parser.h2
-rw-r--r--src/settings/plugins/ifupdown/plugin.c36
-rw-r--r--src/settings/plugins/ifupdown/plugin.h2
-rw-r--r--src/settings/plugins/keyfile/plugin.c5
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c146
-rw-r--r--src/settings/plugins/keyfile/utils.h2
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c10
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.c34
-rw-r--r--src/supplicant-manager/nm-supplicant-settings-verify.c2
-rw-r--r--src/supplicant-manager/tests/test-supplicant-config.c70
-rw-r--r--src/systemd/nm-sd-adapt.c216
-rw-r--r--src/systemd/nm-sd-adapt.h23
-rw-r--r--src/systemd/nm-sd.c136
-rw-r--r--src/systemd/nm-sd.h25
-rw-r--r--src/systemd/src/basic/alloc-util.h22
-rw-r--r--src/systemd/src/basic/escape.c28
-rw-r--r--src/systemd/src/basic/escape.h1
-rw-r--r--src/systemd/src/basic/ether-addr-util.c58
-rw-r--r--src/systemd/src/basic/ether-addr-util.h37
-rw-r--r--src/systemd/src/basic/fileio.c12
-rw-r--r--src/systemd/src/basic/fs-util.c18
-rw-r--r--src/systemd/src/basic/fs-util.h1
-rw-r--r--src/systemd/src/basic/hashmap.c14
-rw-r--r--src/systemd/src/basic/hexdecoct.c13
-rw-r--r--src/systemd/src/basic/hostname-util.c36
-rw-r--r--src/systemd/src/basic/hostname-util.h1
-rw-r--r--src/systemd/src/basic/io-util.c2
-rw-r--r--src/systemd/src/basic/io-util.h2
-rw-r--r--src/systemd/src/basic/list.h14
-rw-r--r--src/systemd/src/basic/log.h3
-rw-r--r--src/systemd/src/basic/macro.h15
-rw-r--r--src/systemd/src/basic/mempool.h2
-rw-r--r--src/systemd/src/basic/parse-util.c2
-rw-r--r--src/systemd/src/basic/set.h3
-rw-r--r--src/systemd/src/basic/socket-util.c982
-rw-r--r--src/systemd/src/basic/socket-util.h2
-rw-r--r--src/systemd/src/basic/stdio-util.h2
-rw-r--r--src/systemd/src/basic/string-util.c2
-rw-r--r--src/systemd/src/basic/strv.c4
-rw-r--r--src/systemd/src/basic/time-util.c95
-rw-r--r--src/systemd/src/basic/time-util.h1
-rw-r--r--src/systemd/src/basic/utf8.c2
-rw-r--r--src/systemd/src/basic/util.h11
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h4
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-option.c13
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-packet.c2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c3
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.c362
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.h78
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.c797
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.h106
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c35
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.h4
-rw-r--r--src/systemd/src/libsystemd-network/lldp-port.c118
-rw-r--r--src/systemd/src/libsystemd-network/lldp-port.h69
-rw-r--r--src/systemd/src/libsystemd-network/lldp-tlv.c640
-rw-r--r--src/systemd/src/libsystemd-network/lldp-tlv.h94
-rw-r--r--src/systemd/src/libsystemd-network/lldp.h84
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c4
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c32
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c19
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c8
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c8
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c898
-rw-r--r--src/systemd/src/libsystemd/sd-event/sd-event.c2905
-rw-r--r--src/systemd/src/libsystemd/sd-id128/sd-id128.c2
-rw-r--r--src/systemd/src/shared/dns-domain.c8
-rw-r--r--src/systemd/src/shared/dns-domain.h5
-rw-r--r--src/systemd/src/systemd/_sd-common.h2
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h6
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h7
-rw-r--r--src/systemd/src/systemd/sd-event.h6
-rw-r--r--src/systemd/src/systemd/sd-id128.h4
-rw-r--r--src/systemd/src/systemd/sd-ipv4acd.h6
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h6
-rw-r--r--src/systemd/src/systemd/sd-lldp.h90
-rw-r--r--src/systemd/src/systemd/sd-ndisc.h2
-rw-r--r--src/tests/Makefile.am1
-rw-r--r--src/tests/Makefile.in1
-rw-r--r--src/tests/config/test-config.c16
-rw-r--r--src/tests/test-general-with-expect.c8
-rw-r--r--src/tests/test-general.c27
-rw-r--r--src/tests/test-route-manager.c36
-rw-r--r--src/tests/test-systemd.c87
-rw-r--r--src/vpn-manager/nm-vpn-connection.c29
217 files changed, 11739 insertions, 5444 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index eea646a4..aee7eae9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -64,6 +64,8 @@ SYSTEMD_NM_CFLAGS_PATHS = \
 	-I$(top_srcdir)/src/systemd
 
 libsystemd_nm_la_SOURCES = \
+	systemd/nm-sd.c \
+	systemd/nm-sd.h \
 	systemd/nm-sd-adapt.c \
 	systemd/nm-sd-adapt.h \
 	systemd/src/basic/alloc-util.c \
@@ -71,6 +73,8 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/async.h \
 	systemd/src/basic/escape.c \
 	systemd/src/basic/escape.h \
+	systemd/src/basic/ether-addr-util.c \
+	systemd/src/basic/ether-addr-util.h \
 	systemd/src/basic/fd-util.c \
 	systemd/src/basic/fd-util.h \
 	systemd/src/basic/fileio.c \
@@ -106,6 +110,7 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/set.h \
 	systemd/src/basic/siphash24.c \
 	systemd/src/basic/siphash24.h \
+	systemd/src/basic/socket-util.c \
 	systemd/src/basic/socket-util.h \
 	systemd/src/basic/sparse-endian.h \
 	systemd/src/basic/stdio-util.h \
@@ -138,14 +143,11 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/libsystemd-network/dhcp6-network.c \
 	systemd/src/libsystemd-network/dhcp6-option.c \
 	systemd/src/libsystemd-network/dhcp6-protocol.h \
-	systemd/src/libsystemd-network/lldp-internal.c \
 	systemd/src/libsystemd-network/lldp-internal.h \
+	systemd/src/libsystemd-network/lldp-neighbor.c \
+	systemd/src/libsystemd-network/lldp-neighbor.h \
 	systemd/src/libsystemd-network/lldp-network.c \
 	systemd/src/libsystemd-network/lldp-network.h \
-	systemd/src/libsystemd-network/lldp-port.c \
-	systemd/src/libsystemd-network/lldp-port.h \
-	systemd/src/libsystemd-network/lldp-tlv.c \
-	systemd/src/libsystemd-network/lldp-tlv.h \
 	systemd/src/libsystemd-network/lldp.h \
 	systemd/src/libsystemd-network/network-internal.c \
 	systemd/src/libsystemd-network/network-internal.h \
@@ -156,6 +158,7 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/libsystemd-network/sd-ipv4acd.c \
 	systemd/src/libsystemd-network/sd-ipv4ll.c \
 	systemd/src/libsystemd-network/sd-lldp.c \
+	systemd/src/libsystemd/sd-event/sd-event.c \
 	systemd/src/libsystemd/sd-id128/sd-id128.c \
 	systemd/src/shared/dns-domain.c \
 	systemd/src/shared/dns-domain.h \
@@ -287,6 +290,7 @@ libNetworkManager_la_SOURCES = \
 	\
 	dhcp-manager/nm-dhcp-client.c \
 	dhcp-manager/nm-dhcp-client.h \
+	dhcp-manager/nm-dhcp-client-logging.h \
 	dhcp-manager/nm-dhcp-utils.c \
 	dhcp-manager/nm-dhcp-utils.h \
 	dhcp-manager/nm-dhcp-listener.c \
@@ -310,6 +314,8 @@ libNetworkManager_la_SOURCES = \
 	dnsmasq-manager/nm-dnsmasq-utils.c \
 	dnsmasq-manager/nm-dnsmasq-utils.h \
 	\
+	platform/nmp-netns.c \
+	platform/nmp-netns.h \
 	platform/nm-fake-platform.c \
 	platform/nm-fake-platform.h \
 	platform/nm-linux-platform.c \
@@ -462,6 +468,8 @@ nm_enum_types_sources = $(filter-out \
                               %/nm-device-private.h \
                               %/nm-rdisc-private.h \
                               %/wifi-utils-private.h \
+                              %/nm-dhcp-client-logging.h \
+                              %/nm-device-logging.h \
                               , $(libNetworkManager_la_SOURCES))
 
 BUILT_SOURCES = $(GLIB_GENERATED)
@@ -519,13 +527,16 @@ if WITH_LIBSOUP
 libNetworkManager_la_LIBADD += $(LIBSOUP_LIBS)
 endif
 
-NetworkManager_LDFLAGS = -rdynamic
+NetworkManager_LDFLAGS = \
+	-rdynamic \
+	-Wl,--version-script="$(srcdir)/NetworkManager.ver"
 
 ######################
 
 libnm_iface_helper_la_SOURCES = \
 	dhcp-manager/nm-dhcp-client.c \
 	dhcp-manager/nm-dhcp-client.h \
+	dhcp-manager/nm-dhcp-client-logging.h \
 	dhcp-manager/nm-dhcp-utils.c \
 	dhcp-manager/nm-dhcp-utils.h \
 	dhcp-manager/nm-dhcp-manager.c \
@@ -537,6 +548,8 @@ libnm_iface_helper_la_SOURCES = \
 	platform/nm-platform.h \
 	platform/nm-platform-utils.c \
 	platform/nm-platform-utils.h \
+	platform/nmp-netns.c \
+	platform/nmp-netns.h \
 	platform/nmp-object.c \
 	platform/nmp-object.h \
 	platform/wifi/wifi-utils-nl80211.c \
@@ -619,12 +632,13 @@ dbusservice_DATA = org.freedesktop.NetworkManager.conf
 
 EXTRA_DIST = \
 	$(dbusservice_DATA) \
-	$(NetworkManager_DATA)
+	$(NetworkManager_DATA) \
+	NetworkManager.ver
 
 rundir=$(runstatedir)/NetworkManager
 statedir=$(localstatedir)/lib/NetworkManager
 install-data-hook:
-	$(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir)
+	$(mkinstalldirs) -m 0755 $(DESTDIR)$(rundir)
 	$(mkinstalldirs) -m 0700 $(DESTDIR)$(statedir)
 	$(mkinstalldirs) -m 0755 $(DESTDIR)$(pkglibdir)
 
diff --git a/src/Makefile.in b/src/Makefile.in
index 14ef31f7..df113412 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -188,8 +188,10 @@ am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \
 	devices/nm-device-factory.h devices/nm-device-generic.c \
 	devices/nm-device-generic.h devices/nm-device-logging.h \
 	devices/nm-device-private.h dhcp-manager/nm-dhcp-client.c \
-	dhcp-manager/nm-dhcp-client.h dhcp-manager/nm-dhcp-utils.c \
-	dhcp-manager/nm-dhcp-utils.h dhcp-manager/nm-dhcp-listener.c \
+	dhcp-manager/nm-dhcp-client.h \
+	dhcp-manager/nm-dhcp-client-logging.h \
+	dhcp-manager/nm-dhcp-utils.c dhcp-manager/nm-dhcp-utils.h \
+	dhcp-manager/nm-dhcp-listener.c \
 	dhcp-manager/nm-dhcp-listener.h dhcp-manager/nm-dhcp-manager.c \
 	dhcp-manager/nm-dhcp-manager.h dns-manager/nm-dns-dnsmasq.c \
 	dns-manager/nm-dns-dnsmasq.h dns-manager/nm-dns-unbound.c \
@@ -200,7 +202,8 @@ am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \
 	dnsmasq-manager/nm-dnsmasq-manager.c \
 	dnsmasq-manager/nm-dnsmasq-manager.h \
 	dnsmasq-manager/nm-dnsmasq-utils.c \
-	dnsmasq-manager/nm-dnsmasq-utils.h platform/nm-fake-platform.c \
+	dnsmasq-manager/nm-dnsmasq-utils.h platform/nmp-netns.c \
+	platform/nmp-netns.h platform/nm-fake-platform.c \
 	platform/nm-fake-platform.h platform/nm-linux-platform.c \
 	platform/nm-linux-platform.h platform/nm-platform.c \
 	platform/nm-platform.h platform/nm-platform-utils.c \
@@ -278,27 +281,28 @@ am_libNetworkManager_la_OBJECTS = $(am__objects_1) $(am__objects_1) \
 	nm-dhcp-listener.lo nm-dhcp-manager.lo nm-dns-dnsmasq.lo \
 	nm-dns-unbound.lo nm-dns-manager.lo nm-dns-plugin.lo \
 	nm-dns-utils.lo nm-dnsmasq-manager.lo nm-dnsmasq-utils.lo \
-	nm-fake-platform.lo nm-linux-platform.lo nm-platform.lo \
-	nm-platform-utils.lo nmp-object.lo wifi-utils-nl80211.lo \
-	wifi-utils.lo nm-fake-rdisc.lo nm-lndp-rdisc.lo nm-rdisc.lo \
-	nm-ppp-manager.lo nm-agent-manager.lo nm-inotify-helper.lo \
-	nm-secret-agent.lo nm-settings-connection.lo \
-	nm-settings-plugin.lo nm-settings.lo nm-keyfile-connection.lo \
-	plugin.lo reader.lo utils.lo writer.lo nm-supplicant-config.lo \
-	nm-supplicant-interface.lo nm-supplicant-manager.lo \
-	nm-supplicant-settings-verify.lo nm-vpn-connection.lo \
-	nm-vpn-manager.lo nm-activation-request.lo \
-	nm-active-connection.lo nm-audit-manager.lo nm-bus-manager.lo \
-	nm-config.lo nm-config-data.lo nm-connection-provider.lo \
-	nm-connectivity.lo nm-dcb.lo nm-route-manager.lo \
-	nm-default-route-manager.lo nm-dhcp4-config.lo \
-	nm-dhcp6-config.lo nm-dispatcher.lo nm-enum-types.lo \
-	nm-exported-object.lo nm-firewall-manager.lo nm-ip4-config.lo \
-	nm-ip6-config.lo nm-logging.lo nm-auth-manager.lo \
-	nm-auth-subject.lo nm-auth-utils.lo nm-manager.lo \
-	nm-multi-index.lo nm-policy.lo nm-rfkill-manager.lo \
-	nm-session-monitor.lo nm-core-utils.lo NetworkManagerUtils.lo \
-	$(am__objects_2) $(am__objects_3) $(am__objects_4)
+	nmp-netns.lo nm-fake-platform.lo nm-linux-platform.lo \
+	nm-platform.lo nm-platform-utils.lo nmp-object.lo \
+	wifi-utils-nl80211.lo wifi-utils.lo nm-fake-rdisc.lo \
+	nm-lndp-rdisc.lo nm-rdisc.lo nm-ppp-manager.lo \
+	nm-agent-manager.lo nm-inotify-helper.lo nm-secret-agent.lo \
+	nm-settings-connection.lo nm-settings-plugin.lo nm-settings.lo \
+	nm-keyfile-connection.lo plugin.lo reader.lo utils.lo \
+	writer.lo nm-supplicant-config.lo nm-supplicant-interface.lo \
+	nm-supplicant-manager.lo nm-supplicant-settings-verify.lo \
+	nm-vpn-connection.lo nm-vpn-manager.lo \
+	nm-activation-request.lo nm-active-connection.lo \
+	nm-audit-manager.lo nm-bus-manager.lo nm-config.lo \
+	nm-config-data.lo nm-connection-provider.lo nm-connectivity.lo \
+	nm-dcb.lo nm-route-manager.lo nm-default-route-manager.lo \
+	nm-dhcp4-config.lo nm-dhcp6-config.lo nm-dispatcher.lo \
+	nm-enum-types.lo nm-exported-object.lo nm-firewall-manager.lo \
+	nm-ip4-config.lo nm-ip6-config.lo nm-logging.lo \
+	nm-auth-manager.lo nm-auth-subject.lo nm-auth-utils.lo \
+	nm-manager.lo nm-multi-index.lo nm-policy.lo \
+	nm-rfkill-manager.lo nm-session-monitor.lo nm-core-utils.lo \
+	NetworkManagerUtils.lo $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4)
 libNetworkManager_la_OBJECTS = $(am_libNetworkManager_la_OBJECTS)
 libnm_iface_helper_la_DEPENDENCIES =  \
 	$(top_builddir)/libnm-core/libnm-core.la \
@@ -308,11 +312,13 @@ libnm_iface_helper_la_DEPENDENCIES =  \
 	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am__libnm_iface_helper_la_SOURCES_DIST =  \
 	dhcp-manager/nm-dhcp-client.c dhcp-manager/nm-dhcp-client.h \
+	dhcp-manager/nm-dhcp-client-logging.h \
 	dhcp-manager/nm-dhcp-utils.c dhcp-manager/nm-dhcp-utils.h \
 	dhcp-manager/nm-dhcp-manager.c dhcp-manager/nm-dhcp-manager.h \
 	platform/nm-linux-platform.c platform/nm-linux-platform.h \
 	platform/nm-platform.c platform/nm-platform.h \
 	platform/nm-platform-utils.c platform/nm-platform-utils.h \
+	platform/nmp-netns.c platform/nmp-netns.h \
 	platform/nmp-object.c platform/nmp-object.h \
 	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
@@ -330,16 +336,17 @@ am__libnm_iface_helper_la_SOURCES_DIST =  \
 	platform/wifi/wifi-utils-wext.h
 am_libnm_iface_helper_la_OBJECTS = nm-dhcp-client.lo nm-dhcp-utils.lo \
 	nm-dhcp-manager.lo nm-linux-platform.lo nm-platform.lo \
-	nm-platform-utils.lo nmp-object.lo wifi-utils-nl80211.lo \
-	wifi-utils.lo nm-fake-rdisc.lo nm-lndp-rdisc.lo nm-rdisc.lo \
-	nm-route-manager.lo nm-exported-object.lo nm-ip4-config.lo \
-	nm-ip6-config.lo nm-enum-types.lo nm-logging.lo \
-	nm-multi-index.lo nm-core-utils.lo NetworkManagerUtils.lo \
-	$(am__objects_4)
+	nm-platform-utils.lo nmp-netns.lo nmp-object.lo \
+	wifi-utils-nl80211.lo wifi-utils.lo nm-fake-rdisc.lo \
+	nm-lndp-rdisc.lo nm-rdisc.lo nm-route-manager.lo \
+	nm-exported-object.lo nm-ip4-config.lo nm-ip6-config.lo \
+	nm-enum-types.lo nm-logging.lo nm-multi-index.lo \
+	nm-core-utils.lo NetworkManagerUtils.lo $(am__objects_4)
 libnm_iface_helper_la_OBJECTS = $(am_libnm_iface_helper_la_OBJECTS)
 libsystemd_nm_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
-am_libsystemd_nm_la_OBJECTS = libsystemd_nm_la-nm-sd-adapt.lo \
-	libsystemd_nm_la-alloc-util.lo libsystemd_nm_la-escape.lo \
+am_libsystemd_nm_la_OBJECTS = libsystemd_nm_la-nm-sd.lo \
+	libsystemd_nm_la-nm-sd-adapt.lo libsystemd_nm_la-alloc-util.lo \
+	libsystemd_nm_la-escape.lo libsystemd_nm_la-ether-addr-util.lo \
 	libsystemd_nm_la-fd-util.lo libsystemd_nm_la-fileio.lo \
 	libsystemd_nm_la-fs-util.lo libsystemd_nm_la-hash-funcs.lo \
 	libsystemd_nm_la-hashmap.lo libsystemd_nm_la-hexdecoct.lo \
@@ -348,6 +355,7 @@ am_libsystemd_nm_la_OBJECTS = libsystemd_nm_la-nm-sd-adapt.lo \
 	libsystemd_nm_la-mempool.lo libsystemd_nm_la-parse-util.lo \
 	libsystemd_nm_la-path-util.lo libsystemd_nm_la-prioq.lo \
 	libsystemd_nm_la-random-util.lo libsystemd_nm_la-siphash24.lo \
+	libsystemd_nm_la-socket-util.lo \
 	libsystemd_nm_la-string-table.lo \
 	libsystemd_nm_la-string-util.lo libsystemd_nm_la-strv.lo \
 	libsystemd_nm_la-time-util.lo libsystemd_nm_la-utf8.lo \
@@ -358,17 +366,16 @@ am_libsystemd_nm_la_OBJECTS = libsystemd_nm_la-nm-sd-adapt.lo \
 	libsystemd_nm_la-dhcp-packet.lo \
 	libsystemd_nm_la-dhcp6-network.lo \
 	libsystemd_nm_la-dhcp6-option.lo \
-	libsystemd_nm_la-lldp-internal.lo \
-	libsystemd_nm_la-lldp-network.lo libsystemd_nm_la-lldp-port.lo \
-	libsystemd_nm_la-lldp-tlv.lo \
+	libsystemd_nm_la-lldp-neighbor.lo \
+	libsystemd_nm_la-lldp-network.lo \
 	libsystemd_nm_la-network-internal.lo \
 	libsystemd_nm_la-sd-dhcp-client.lo \
 	libsystemd_nm_la-sd-dhcp-lease.lo \
 	libsystemd_nm_la-sd-dhcp6-client.lo \
 	libsystemd_nm_la-sd-dhcp6-lease.lo \
 	libsystemd_nm_la-sd-ipv4acd.lo libsystemd_nm_la-sd-ipv4ll.lo \
-	libsystemd_nm_la-sd-lldp.lo libsystemd_nm_la-sd-id128.lo \
-	libsystemd_nm_la-dns-domain.lo
+	libsystemd_nm_la-sd-lldp.lo libsystemd_nm_la-sd-event.lo \
+	libsystemd_nm_la-sd-id128.lo libsystemd_nm_la-dns-domain.lo
 libsystemd_nm_la_OBJECTS = $(am_libsystemd_nm_la_OBJECTS)
 am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \
 	"$(DESTDIR)$(dbusservicedir)"
@@ -852,6 +859,8 @@ SYSTEMD_NM_CFLAGS_PATHS = \
 	-I$(top_srcdir)/src/systemd
 
 libsystemd_nm_la_SOURCES = \
+	systemd/nm-sd.c \
+	systemd/nm-sd.h \
 	systemd/nm-sd-adapt.c \
 	systemd/nm-sd-adapt.h \
 	systemd/src/basic/alloc-util.c \
@@ -859,6 +868,8 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/async.h \
 	systemd/src/basic/escape.c \
 	systemd/src/basic/escape.h \
+	systemd/src/basic/ether-addr-util.c \
+	systemd/src/basic/ether-addr-util.h \
 	systemd/src/basic/fd-util.c \
 	systemd/src/basic/fd-util.h \
 	systemd/src/basic/fileio.c \
@@ -894,6 +905,7 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/basic/set.h \
 	systemd/src/basic/siphash24.c \
 	systemd/src/basic/siphash24.h \
+	systemd/src/basic/socket-util.c \
 	systemd/src/basic/socket-util.h \
 	systemd/src/basic/sparse-endian.h \
 	systemd/src/basic/stdio-util.h \
@@ -926,14 +938,11 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/libsystemd-network/dhcp6-network.c \
 	systemd/src/libsystemd-network/dhcp6-option.c \
 	systemd/src/libsystemd-network/dhcp6-protocol.h \
-	systemd/src/libsystemd-network/lldp-internal.c \
 	systemd/src/libsystemd-network/lldp-internal.h \
+	systemd/src/libsystemd-network/lldp-neighbor.c \
+	systemd/src/libsystemd-network/lldp-neighbor.h \
 	systemd/src/libsystemd-network/lldp-network.c \
 	systemd/src/libsystemd-network/lldp-network.h \
-	systemd/src/libsystemd-network/lldp-port.c \
-	systemd/src/libsystemd-network/lldp-port.h \
-	systemd/src/libsystemd-network/lldp-tlv.c \
-	systemd/src/libsystemd-network/lldp-tlv.h \
 	systemd/src/libsystemd-network/lldp.h \
 	systemd/src/libsystemd-network/network-internal.c \
 	systemd/src/libsystemd-network/network-internal.h \
@@ -944,6 +953,7 @@ libsystemd_nm_la_SOURCES = \
 	systemd/src/libsystemd-network/sd-ipv4acd.c \
 	systemd/src/libsystemd-network/sd-ipv4ll.c \
 	systemd/src/libsystemd-network/sd-lldp.c \
+	systemd/src/libsystemd/sd-event/sd-event.c \
 	systemd/src/libsystemd/sd-id128/sd-id128.c \
 	systemd/src/shared/dns-domain.c \
 	systemd/src/shared/dns-domain.h \
@@ -1048,8 +1058,10 @@ libNetworkManager_la_SOURCES = $(nm_device_headers) \
 	devices/nm-device-factory.h devices/nm-device-generic.c \
 	devices/nm-device-generic.h devices/nm-device-logging.h \
 	devices/nm-device-private.h dhcp-manager/nm-dhcp-client.c \
-	dhcp-manager/nm-dhcp-client.h dhcp-manager/nm-dhcp-utils.c \
-	dhcp-manager/nm-dhcp-utils.h dhcp-manager/nm-dhcp-listener.c \
+	dhcp-manager/nm-dhcp-client.h \
+	dhcp-manager/nm-dhcp-client-logging.h \
+	dhcp-manager/nm-dhcp-utils.c dhcp-manager/nm-dhcp-utils.h \
+	dhcp-manager/nm-dhcp-listener.c \
 	dhcp-manager/nm-dhcp-listener.h dhcp-manager/nm-dhcp-manager.c \
 	dhcp-manager/nm-dhcp-manager.h dns-manager/nm-dns-dnsmasq.c \
 	dns-manager/nm-dns-dnsmasq.h dns-manager/nm-dns-unbound.c \
@@ -1060,7 +1072,8 @@ libNetworkManager_la_SOURCES = $(nm_device_headers) \
 	dnsmasq-manager/nm-dnsmasq-manager.c \
 	dnsmasq-manager/nm-dnsmasq-manager.h \
 	dnsmasq-manager/nm-dnsmasq-utils.c \
-	dnsmasq-manager/nm-dnsmasq-utils.h platform/nm-fake-platform.c \
+	dnsmasq-manager/nm-dnsmasq-utils.h platform/nmp-netns.c \
+	platform/nmp-netns.h platform/nm-fake-platform.c \
 	platform/nm-fake-platform.h platform/nm-linux-platform.c \
 	platform/nm-linux-platform.h platform/nm-platform.c \
 	platform/nm-platform.h platform/nm-platform-utils.c \
@@ -1130,6 +1143,8 @@ nm_enum_types_sources = $(filter-out \
                               %/nm-device-private.h \
                               %/nm-rdisc-private.h \
                               %/wifi-utils-private.h \
+                              %/nm-dhcp-client-logging.h \
+                              %/nm-device-logging.h \
                               , $(libNetworkManager_la_SOURCES))
 
 BUILT_SOURCES = $(GLIB_GENERATED)
@@ -1139,17 +1154,23 @@ libNetworkManager_la_LIBADD =  \
 	$(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) $(SYSTEMD_LOGIN_LIBS) \
 	$(SYSTEMD_JOURNAL_LIBS) $(LIBNDP_LIBS) $(LIBDL) $(LIBM) \
 	$(SELINUX_LIBS) $(LIBAUDIT_LIBS) $(am__append_9)
-NetworkManager_LDFLAGS = -rdynamic
+NetworkManager_LDFLAGS = \
+	-rdynamic \
+	-Wl,--version-script="$(srcdir)/NetworkManager.ver"
+
 
 ######################
 libnm_iface_helper_la_SOURCES = dhcp-manager/nm-dhcp-client.c \
-	dhcp-manager/nm-dhcp-client.h dhcp-manager/nm-dhcp-utils.c \
-	dhcp-manager/nm-dhcp-utils.h dhcp-manager/nm-dhcp-manager.c \
-	dhcp-manager/nm-dhcp-manager.h platform/nm-linux-platform.c \
-	platform/nm-linux-platform.h platform/nm-platform.c \
-	platform/nm-platform.h platform/nm-platform-utils.c \
-	platform/nm-platform-utils.h platform/nmp-object.c \
-	platform/nmp-object.h platform/wifi/wifi-utils-nl80211.c \
+	dhcp-manager/nm-dhcp-client.h \
+	dhcp-manager/nm-dhcp-client-logging.h \
+	dhcp-manager/nm-dhcp-utils.c dhcp-manager/nm-dhcp-utils.h \
+	dhcp-manager/nm-dhcp-manager.c dhcp-manager/nm-dhcp-manager.h \
+	platform/nm-linux-platform.c platform/nm-linux-platform.h \
+	platform/nm-platform.c platform/nm-platform.h \
+	platform/nm-platform-utils.c platform/nm-platform-utils.h \
+	platform/nmp-netns.c platform/nmp-netns.h \
+	platform/nmp-object.c platform/nmp-object.h \
+	platform/wifi/wifi-utils-nl80211.c \
 	platform/wifi/wifi-utils-nl80211.h \
 	platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \
 	platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \
@@ -1197,7 +1218,8 @@ dbusservicedir = $(DBUS_SYS_DIR)
 dbusservice_DATA = org.freedesktop.NetworkManager.conf
 EXTRA_DIST = \
 	$(dbusservice_DATA) \
-	$(NetworkManager_DATA)
+	$(NetworkManager_DATA) \
+	NetworkManager.ver
 
 rundir = $(runstatedir)/NetworkManager
 statedir = $(localstatedir)/lib/NetworkManager
@@ -1387,6 +1409,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-dhcp6-option.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-dns-domain.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-escape.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-ether-addr-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-fd-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-fileio.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-fs-util.Plo@am__quote@
@@ -1396,13 +1419,12 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-hostname-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-in-addr-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-io-util.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-lldp-internal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-lldp-neighbor.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-lldp-network.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-lldp-port.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-lldp-tlv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-mempool.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-network-internal.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-nm-sd.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-parse-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-path-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-prioq.Plo@am__quote@
@@ -1411,11 +1433,13 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-dhcp-lease.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-dhcp6-client.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-dhcp6-lease.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-event.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-id128.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-ipv4acd.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-ipv4ll.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-sd-lldp.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-siphash24.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-socket-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-string-table.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-string-util.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsystemd_nm_la-strv.Plo@am__quote@
@@ -1508,6 +1532,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-supplicant-settings-verify.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-connection.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-manager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmp-netns.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmp-object.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reader.Plo@am__quote@
@@ -1671,6 +1696,13 @@ nm-dnsmasq-utils.lo: dnsmasq-manager/nm-dnsmasq-utils.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-dnsmasq-utils.lo `test -f 'dnsmasq-manager/nm-dnsmasq-utils.c' || echo '$(srcdir)/'`dnsmasq-manager/nm-dnsmasq-utils.c
 
+nmp-netns.lo: platform/nmp-netns.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmp-netns.lo -MD -MP -MF $(DEPDIR)/nmp-netns.Tpo -c -o nmp-netns.lo `test -f 'platform/nmp-netns.c' || echo '$(srcdir)/'`platform/nmp-netns.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nmp-netns.Tpo $(DEPDIR)/nmp-netns.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='platform/nmp-netns.c' object='nmp-netns.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmp-netns.lo `test -f 'platform/nmp-netns.c' || echo '$(srcdir)/'`platform/nmp-netns.c
+
 nm-fake-platform.lo: platform/nm-fake-platform.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-fake-platform.lo -MD -MP -MF $(DEPDIR)/nm-fake-platform.Tpo -c -o nm-fake-platform.lo `test -f 'platform/nm-fake-platform.c' || echo '$(srcdir)/'`platform/nm-fake-platform.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-fake-platform.Tpo $(DEPDIR)/nm-fake-platform.Plo
@@ -1874,6 +1906,13 @@ wifi-utils-wext.lo: platform/wifi/wifi-utils-wext.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wifi-utils-wext.lo `test -f 'platform/wifi/wifi-utils-wext.c' || echo '$(srcdir)/'`platform/wifi/wifi-utils-wext.c
 
+libsystemd_nm_la-nm-sd.lo: systemd/nm-sd.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-nm-sd.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-nm-sd.Tpo -c -o libsystemd_nm_la-nm-sd.lo `test -f 'systemd/nm-sd.c' || echo '$(srcdir)/'`systemd/nm-sd.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-nm-sd.Tpo $(DEPDIR)/libsystemd_nm_la-nm-sd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/nm-sd.c' object='libsystemd_nm_la-nm-sd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-nm-sd.lo `test -f 'systemd/nm-sd.c' || echo '$(srcdir)/'`systemd/nm-sd.c
+
 libsystemd_nm_la-nm-sd-adapt.lo: systemd/nm-sd-adapt.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-nm-sd-adapt.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Tpo -c -o libsystemd_nm_la-nm-sd-adapt.lo `test -f 'systemd/nm-sd-adapt.c' || echo '$(srcdir)/'`systemd/nm-sd-adapt.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Tpo $(DEPDIR)/libsystemd_nm_la-nm-sd-adapt.Plo
@@ -1895,6 +1934,13 @@ libsystemd_nm_la-escape.lo: systemd/src/basic/escape.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-escape.lo `test -f 'systemd/src/basic/escape.c' || echo '$(srcdir)/'`systemd/src/basic/escape.c
 
+libsystemd_nm_la-ether-addr-util.lo: systemd/src/basic/ether-addr-util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-ether-addr-util.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-ether-addr-util.Tpo -c -o libsystemd_nm_la-ether-addr-util.lo `test -f 'systemd/src/basic/ether-addr-util.c' || echo '$(srcdir)/'`systemd/src/basic/ether-addr-util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-ether-addr-util.Tpo $(DEPDIR)/libsystemd_nm_la-ether-addr-util.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/basic/ether-addr-util.c' object='libsystemd_nm_la-ether-addr-util.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-ether-addr-util.lo `test -f 'systemd/src/basic/ether-addr-util.c' || echo '$(srcdir)/'`systemd/src/basic/ether-addr-util.c
+
 libsystemd_nm_la-fd-util.lo: systemd/src/basic/fd-util.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-fd-util.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-fd-util.Tpo -c -o libsystemd_nm_la-fd-util.lo `test -f 'systemd/src/basic/fd-util.c' || echo '$(srcdir)/'`systemd/src/basic/fd-util.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-fd-util.Tpo $(DEPDIR)/libsystemd_nm_la-fd-util.Plo
@@ -2000,6 +2046,13 @@ libsystemd_nm_la-siphash24.lo: systemd/src/basic/siphash24.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-siphash24.lo `test -f 'systemd/src/basic/siphash24.c' || echo '$(srcdir)/'`systemd/src/basic/siphash24.c
 
+libsystemd_nm_la-socket-util.lo: systemd/src/basic/socket-util.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-socket-util.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-socket-util.Tpo -c -o libsystemd_nm_la-socket-util.lo `test -f 'systemd/src/basic/socket-util.c' || echo '$(srcdir)/'`systemd/src/basic/socket-util.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-socket-util.Tpo $(DEPDIR)/libsystemd_nm_la-socket-util.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/basic/socket-util.c' object='libsystemd_nm_la-socket-util.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-socket-util.lo `test -f 'systemd/src/basic/socket-util.c' || echo '$(srcdir)/'`systemd/src/basic/socket-util.c
+
 libsystemd_nm_la-string-table.lo: systemd/src/basic/string-table.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-string-table.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-string-table.Tpo -c -o libsystemd_nm_la-string-table.lo `test -f 'systemd/src/basic/string-table.c' || echo '$(srcdir)/'`systemd/src/basic/string-table.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-string-table.Tpo $(DEPDIR)/libsystemd_nm_la-string-table.Plo
@@ -2091,12 +2144,12 @@ libsystemd_nm_la-dhcp6-option.lo: systemd/src/libsystemd-network/dhcp6-option.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-dhcp6-option.lo `test -f 'systemd/src/libsystemd-network/dhcp6-option.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/dhcp6-option.c
 
-libsystemd_nm_la-lldp-internal.lo: systemd/src/libsystemd-network/lldp-internal.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-lldp-internal.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-lldp-internal.Tpo -c -o libsystemd_nm_la-lldp-internal.lo `test -f 'systemd/src/libsystemd-network/lldp-internal.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-internal.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-lldp-internal.Tpo $(DEPDIR)/libsystemd_nm_la-lldp-internal.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/libsystemd-network/lldp-internal.c' object='libsystemd_nm_la-lldp-internal.lo' libtool=yes @AMDEPBACKSLASH@
+libsystemd_nm_la-lldp-neighbor.lo: systemd/src/libsystemd-network/lldp-neighbor.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-lldp-neighbor.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-lldp-neighbor.Tpo -c -o libsystemd_nm_la-lldp-neighbor.lo `test -f 'systemd/src/libsystemd-network/lldp-neighbor.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-neighbor.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-lldp-neighbor.Tpo $(DEPDIR)/libsystemd_nm_la-lldp-neighbor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/libsystemd-network/lldp-neighbor.c' object='libsystemd_nm_la-lldp-neighbor.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-lldp-internal.lo `test -f 'systemd/src/libsystemd-network/lldp-internal.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-internal.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-lldp-neighbor.lo `test -f 'systemd/src/libsystemd-network/lldp-neighbor.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-neighbor.c
 
 libsystemd_nm_la-lldp-network.lo: systemd/src/libsystemd-network/lldp-network.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-lldp-network.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-lldp-network.Tpo -c -o libsystemd_nm_la-lldp-network.lo `test -f 'systemd/src/libsystemd-network/lldp-network.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-network.c
@@ -2105,20 +2158,6 @@ libsystemd_nm_la-lldp-network.lo: systemd/src/libsystemd-network/lldp-network.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-lldp-network.lo `test -f 'systemd/src/libsystemd-network/lldp-network.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-network.c
 
-libsystemd_nm_la-lldp-port.lo: systemd/src/libsystemd-network/lldp-port.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-lldp-port.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-lldp-port.Tpo -c -o libsystemd_nm_la-lldp-port.lo `test -f 'systemd/src/libsystemd-network/lldp-port.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-port.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-lldp-port.Tpo $(DEPDIR)/libsystemd_nm_la-lldp-port.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/libsystemd-network/lldp-port.c' object='libsystemd_nm_la-lldp-port.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-lldp-port.lo `test -f 'systemd/src/libsystemd-network/lldp-port.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-port.c
-
-libsystemd_nm_la-lldp-tlv.lo: systemd/src/libsystemd-network/lldp-tlv.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-lldp-tlv.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-lldp-tlv.Tpo -c -o libsystemd_nm_la-lldp-tlv.lo `test -f 'systemd/src/libsystemd-network/lldp-tlv.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-tlv.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-lldp-tlv.Tpo $(DEPDIR)/libsystemd_nm_la-lldp-tlv.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/libsystemd-network/lldp-tlv.c' object='libsystemd_nm_la-lldp-tlv.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-lldp-tlv.lo `test -f 'systemd/src/libsystemd-network/lldp-tlv.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/lldp-tlv.c
-
 libsystemd_nm_la-network-internal.lo: systemd/src/libsystemd-network/network-internal.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-network-internal.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-network-internal.Tpo -c -o libsystemd_nm_la-network-internal.lo `test -f 'systemd/src/libsystemd-network/network-internal.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/network-internal.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-network-internal.Tpo $(DEPDIR)/libsystemd_nm_la-network-internal.Plo
@@ -2175,6 +2214,13 @@ libsystemd_nm_la-sd-lldp.lo: systemd/src/libsystemd-network/sd-lldp.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-sd-lldp.lo `test -f 'systemd/src/libsystemd-network/sd-lldp.c' || echo '$(srcdir)/'`systemd/src/libsystemd-network/sd-lldp.c
 
+libsystemd_nm_la-sd-event.lo: systemd/src/libsystemd/sd-event/sd-event.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-sd-event.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-sd-event.Tpo -c -o libsystemd_nm_la-sd-event.lo `test -f 'systemd/src/libsystemd/sd-event/sd-event.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-event/sd-event.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-sd-event.Tpo $(DEPDIR)/libsystemd_nm_la-sd-event.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='systemd/src/libsystemd/sd-event/sd-event.c' object='libsystemd_nm_la-sd-event.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libsystemd_nm_la-sd-event.lo `test -f 'systemd/src/libsystemd/sd-event/sd-event.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-event/sd-event.c
+
 libsystemd_nm_la-sd-id128.lo: systemd/src/libsystemd/sd-id128/sd-id128.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libsystemd_nm_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libsystemd_nm_la-sd-id128.lo -MD -MP -MF $(DEPDIR)/libsystemd_nm_la-sd-id128.Tpo -c -o libsystemd_nm_la-sd-id128.lo `test -f 'systemd/src/libsystemd/sd-id128/sd-id128.c' || echo '$(srcdir)/'`systemd/src/libsystemd/sd-id128/sd-id128.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libsystemd_nm_la-sd-id128.Tpo $(DEPDIR)/libsystemd_nm_la-sd-id128.Plo
@@ -2708,7 +2754,7 @@ include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
 install-data-hook:
-	$(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir)
+	$(mkinstalldirs) -m 0755 $(DESTDIR)$(rundir)
 	$(mkinstalldirs) -m 0700 $(DESTDIR)$(statedir)
 	$(mkinstalldirs) -m 0755 $(DESTDIR)$(pkglibdir)
 
diff --git a/src/NetworkManager.ver b/src/NetworkManager.ver
new file mode 100644
index 00000000..b4397b9c
--- /dev/null
+++ b/src/NetworkManager.ver
@@ -0,0 +1,9 @@
+{
+global:
+	nm*;
+	_nm*;
+	NM*;
+	_NM*;
+local:
+	*;
+};
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 50fe4efd..09ed25c5 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -127,7 +127,8 @@ get_new_connection_name (const GSList *existing,
 }
 
 static char *
-get_new_connection_ifname (const GSList *existing,
+get_new_connection_ifname (NMPlatform *platform,
+                           const GSList *existing,
                            const char *prefix)
 {
 	int i;
@@ -138,7 +139,7 @@ get_new_connection_ifname (const GSList *existing,
 	for (i = 0; i < 500; i++) {
 		name = g_strdup_printf ("%s%d", prefix, i);
 
-		if (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name))
+		if (nm_platform_link_get_by_ifname (platform, name))
 			goto next;
 
 		for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) {
@@ -205,7 +206,8 @@ nm_utils_get_ip_config_method (NMConnection *connection,
 }
 
 void
-nm_utils_complete_generic (NMConnection *connection,
+nm_utils_complete_generic (NMPlatform *platform,
+                           NMConnection *connection,
                            const char *ctype,
                            const GSList *existing,
                            const char *preferred_id,
@@ -241,7 +243,7 @@ nm_utils_complete_generic (NMConnection *connection,
 
 	/* Add an interface name, if requested */
 	if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) {
-		ifname = get_new_connection_ifname (existing, ifname_prefix);
+		ifname = get_new_connection_ifname (platform, existing, ifname_prefix);
 		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL);
 		g_free (ifname);
 	}
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index b3c384da..9b5b9106 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -28,7 +28,8 @@
 
 const char *nm_utils_get_shared_wifi_permission (NMConnection *connection);
 
-void nm_utils_complete_generic (NMConnection *connection,
+void nm_utils_complete_generic (NMPlatform *platform,
+                                NMConnection *connection,
                                 const char *ctype,
                                 const GSList *existing,
                                 const char *preferred_id,
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index a3fa4901..67085752 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -117,7 +117,7 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
 	nm_log_dbg (LOGD_HW, "(%s): found ATM device", ifname);
 
 	atm_index_path = g_strdup_printf ("/sys/class/atm/%s/atmindex",
-	                                  ASSERT_VALID_PATH_COMPONENT (ifname));
+	                                  NM_ASSERT_VALID_PATH_COMPONENT (ifname));
 	atm_index = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET,
 	                                                      atm_index_path,
 	                                                      10, 0, G_MAXINT,
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index e7a91e0a..ebb7a319 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -125,7 +125,8 @@ complete_connection (NMDevice *device,
 	if (s_adsl && !nm_setting_verify (NM_SETTING (s_adsl), NULL, error))
 		return FALSE;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_ADSL_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
@@ -535,7 +536,7 @@ carrier_update_cb (gpointer user_data)
 	char *path;
 
 	path  = g_strdup_printf ("/sys/class/atm/%s/carrier",
-	                         ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self))));
+	                         NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self))));
 	carrier = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, 1, -1);
 	g_free (path);
 
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 71226813..a009746d 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -246,9 +246,8 @@ pan_connection_check_create (NMBluezDevice *self)
 		priv->pan_connection = added;
 		nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid);
 	} else {
-		nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %d / %s",
-		             priv->path, id, uuid, error ? error->code : -1,
-		             (error && error->message) ? error->message : "(unknown)");
+		nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
+		             priv->path, id, uuid, error->message);
 		g_clear_error (&error);
 
 	}
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index f66b5abe..766ecc0a 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -38,6 +38,16 @@
 #include "nm-platform.h"
 #include "nm-dbus-compat.h"
 
+#define _NMLOG_DOMAIN        LOGD_BT
+#define _NMLOG_PREFIX_NAME   "bluez"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
 typedef struct {
 	int bluez_version;
 
@@ -146,12 +156,12 @@ manager_bdaddr_added_cb (NMBluez4Manager *bluez_mgr,
 	if (!device)
 		return;
 
-	nm_log_info (LOGD_BT, "BT device %s (%s) added (%s%s%s)",
-	             name,
-	             bdaddr,
-	             has_dun ? "DUN" : "",
-	             has_dun && has_nap ? " " : "",
-	             has_nap ? "NAP" : "");
+	_LOGI ("BT device %s (%s) added (%s%s%s)",
+	       name,
+	       bdaddr,
+	       has_dun ? "DUN" : "",
+	       has_dun && has_nap ? " " : "",
+	       has_nap ? "NAP" : "");
 	g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
 	g_object_unref (device);
 }
@@ -163,7 +173,7 @@ setup_version_number (NMBluezManager *self, int bluez_version)
 
 	g_return_if_fail (!priv->bluez_version);
 
-	nm_log_info (LOGD_BT, "use BlueZ version %d", bluez_version);
+	_LOGI ("use BlueZ version %d", bluez_version);
 
 	priv->bluez_version = bluez_version;
 
@@ -235,7 +245,7 @@ check_bluez_and_try_setup_final_step (NMBluezManager *self, int bluez_version, c
 		setup_bluez5 (self);
 		break;
 	default:
-		nm_log_dbg (LOGD_BT, "detecting BlueZ version failed: %s", reason);
+		_LOGD ("detecting BlueZ version failed: %s", reason);
 
 		/* cancel current attempts to detect the version. */
 		cleanup_checking (self, FALSE);
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index deeebcb6..9d8fd210 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -203,7 +203,7 @@ get_properties_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 
 	properties = g_variant_get_child_value (ret, 0);
 
-	g_variant_lookup (properties, "Address", "s", &priv->address);
+	(void) g_variant_lookup (properties, "Address", "s", &priv->address);
 	if (g_variant_lookup (properties, "Devices", "^ao", &devices)) {
 		for (i = 0; devices[i]; i++)
 			device_created (priv->proxy, devices[i], self);
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index e44e87d8..66d171bf 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -213,6 +213,7 @@ get_managed_objects_cb (GDBusProxy *proxy,
 		                            G_VARIANT_TYPE_DICTIONARY)) {
 			device_added (proxy, path, self);
 		}
+		g_variant_unref (ifaces);
 	}
 
 	g_variant_unref (variant);
@@ -231,8 +232,7 @@ on_proxy_acquired (GObject *object,
 	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
 
 	if (!priv->proxy) {
-		nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s",
-		             error && error->message ? error->message : "(unknown)");
+		nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s", error->message);
 		g_clear_error (&error);
 		return;
 	}
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
index ae5512f8..1c531090 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ b/src/devices/bluetooth/nm-bluez5-manager.h
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index e151b795..eef4ed0a 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -20,12 +20,13 @@
 
 #include "nm-default.h"
 
+#include "nm-device-bt.h"
+
 #include <stdio.h>
 #include <string.h>
 
 #include "nm-bluez-common.h"
 #include "nm-bluez-device.h"
-#include "nm-device-bt.h"
 #include "nm-device-private.h"
 #include "ppp-manager/nm-ppp-manager.h"
 #include "nm-setting-connection.h"
@@ -39,6 +40,7 @@
 #include "nm-utils.h"
 #include "nm-bt-error.h"
 #include "nm-bt-enum-types.h"
+#include "nm-platform.h"
 
 #include "nmdbus-device-bt.h"
 
@@ -315,7 +317,8 @@ complete_connection (NMDevice *device,
 		return FALSE;
 	}
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_BLUETOOTH_SETTING_NAME,
 	                           existing_connections,
 	                           preferred,
@@ -497,8 +500,8 @@ modem_ip4_config_result (NMModem *modem,
 
 	if (error) {
 		_LOGW (LOGD_MB | LOGD_IP4 | LOGD_BT,
-		       "retrieving IP4 configuration failed: (%d) %s",
-		       error->code, error->message ? error->message : "(unknown)");
+		       "retrieving IP4 configuration failed: %s",
+		       error->message);
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	} else
@@ -728,8 +731,7 @@ bluez_connect_cb (GObject *object,
 	                                         res, &error);
 
 	if (!device) {
-		_LOGW (LOGD_BT, "Error connecting with bluez: %s",
-		       error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message);
 		g_clear_error (&error);
 
 		nm_device_state_changed (NM_DEVICE (self),
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index cef104e6..b88b2511 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -97,7 +97,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingBond *s_bond;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_BOND_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index cc72b003..c35d2863 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -103,7 +103,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingBridge *s_bridge;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_BRIDGE_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 2fb84276..6225ae7b 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -158,7 +158,7 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	dev = (GUdevDevice *) nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex);
 	if (!dev) {
 		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device %d '%s' with udev",
-		       ifindex, str_if_set (nm_device_get_iface (NM_DEVICE (self)), "(null)"));
+		       ifindex, nm_device_get_iface (NM_DEVICE (self)) ?: "(null)");
 		goto out;
 	}
 	g_object_ref (dev);
@@ -177,7 +177,7 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	dir = g_dir_open (parent_path, 0, &error);
 	if (!dir) {
 		_LOGW (LOGD_DEVICE | LOGD_HW, "failed to open directory '%s': %s",
-		       parent_path, error && error->message ? error->message : "(unknown)");
+		       parent_path, error->message);
 		g_clear_error (&error);
 		goto out;
 	}
@@ -597,13 +597,13 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			if (!success) {
 				_LOGE (LOGD_DEVICE | LOGD_ETHER,
 				       "Activation: (ethernet) couldn't send security configuration to the supplicant: %s",
-				       error ? error->message : "<BUG>");
+				       error->message);
 				g_clear_error (&error);
 			}
 		} else {
 			_LOGE (LOGD_DEVICE | LOGD_ETHER,
 			       "Activation: (ethernet) couldn't build security configuration: %s",
-			       error ? error->message : "<BUG>");
+			       error->message);
 			g_clear_error (&error);
 		}
 
@@ -721,7 +721,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 	NMActRequest *req;
-	NMConnection *connection;
+	NMSettingsConnection *connection;
 	guint64 timestamp = 0;
 	gboolean new_secrets = TRUE;
 
@@ -737,14 +737,14 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
-	connection = nm_act_request_get_applied_connection (req);
+	connection = nm_act_request_get_settings_connection (req);
 	g_assert (connection);
 
 	/* Ask for new secrets only if we've never activated this connection
 	 * before.  If we've connected before, don't bother the user with dialogs,
 	 * just retry or fail, and if we never connect the user can fix the
 	 * password somewhere else. */
-	if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp))
+	if (nm_settings_connection_get_timestamp (connection, &timestamp))
 		new_secrets = !timestamp;
 
 	if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE)
@@ -1187,7 +1187,7 @@ wake_on_lan_enable (NMDevice *device)
 	}
 	wol = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
 found:
-	return nmp_utils_ethtool_set_wake_on_lan (nm_device_get_iface (device), wol, password);
+	return nm_platform_ethtool_set_wake_on_lan (NM_PLATFORM_GET, nm_device_get_iface (device), wol, password);
 }
 
 /****************************************************************/
@@ -1388,7 +1388,8 @@ complete_connection (NMDevice *device,
 	/* Default to an ethernet-only connection, but if a PPPoE setting was given
 	 * then PPPoE should be our connection type.
 	 */
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           s_pppoe ? NM_SETTING_PPPOE_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
@@ -1498,6 +1499,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 		nm_connection_add_setting (connection, (NMSetting *) s_wired);
 	}
 
+	g_object_set (nm_connection_get_setting_connection (connection),
+	              NM_SETTING_CONNECTION_TYPE, nm_connection_get_setting_pppoe (connection)
+	                                          ? NM_SETTING_PPPOE_SETTING_NAME
+	                                          : NM_SETTING_WIRED_SETTING_NAME, NULL);
+
 	/* If the device reports a permanent address, use that for the MAC address
 	 * and the current MAC, if different, is the cloned MAC.
 	 */
@@ -1533,7 +1539,7 @@ get_link_speed (NMDevice *device)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	guint32 speed;
 
-	if (!nmp_utils_ethtool_get_link_speed (nm_device_get_iface (device), &speed))
+	if (!nm_platform_ethtool_get_link_speed (NM_PLATFORM_GET, nm_device_get_iface (device), &speed))
 		return;
 	if (priv->speed == speed)
 		return;
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index bd0ec002..5ce4333f 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -355,7 +355,7 @@ read_device_factory_paths (void)
 	if (!dir) {
 		nm_log_warn (LOGD_HW, "device plugin: failed to open directory %s: %s",
 		             NMPLUGINDIR,
-		             (error && error->message) ? error->message : "(unknown)");
+		             error->message);
 		g_clear_error (&error);
 		return NULL;
 	}
@@ -499,7 +499,7 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call
 		factory = create_func (&error);
 		if (!factory) {
 			nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s",
-			             item, error ? error->message : "unknown");
+			             item, NM_G_ERROR_MSG (error));
 			g_clear_error (&error);
 			g_module_close (plugin);
 			continue;
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 79bcb57c..a27af314 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -85,7 +85,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband);
 
 	mode_path = g_strdup_printf ("/sys/class/net/%s/mode",
-	                             ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (dev)));
+	                             NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (dev)));
 	if (!g_file_test (mode_path, G_FILE_TEST_EXISTS)) {
 		g_free (mode_path);
 
@@ -166,7 +166,8 @@ complete_connection (NMDevice *device,
 	const char *setting_mac;
 	const char *hw_address;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_INFINIBAND_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index 0ba813eb..089b1035 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -338,7 +338,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingIPTunnel *s_ip_tunnel;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_IP_TUNNEL_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
@@ -788,6 +789,12 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 		nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
 }
 
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *dev)
+{
+	return NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
 static void
 unrealize_notify (NMDevice *device)
 {
@@ -876,6 +883,7 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 	device_class->update_connection = update_connection;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->create_and_realize = create_and_realize;
+	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	device_class->realize_start_notify = realize_start_notify;
 	device_class->unrealize_notify = unrealize_notify;
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index 6255d6f3..1be17073 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -34,9 +34,9 @@ _nm_device_log_self_to_device (t *self) \
 #undef  _NMLOG_ENABLED
 #define _NMLOG_ENABLED(level, domain) ( nm_logging_enabled ((level), (domain)) )
 #define _NMLOG(level, domain, ...) \
-    nm_log_obj ((level), (domain), (self), \
+    nm_log_obj ((level), (domain), (self), "device", \
                 "(%s): " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-                (self) ? str_if_set (nm_device_get_iface (_nm_device_log_self_to_device (self)), "(null)") : "(none)" \
+                (self) ? (nm_device_get_iface (_nm_device_log_self_to_device (self)) ?: "(null)") : "(none)" \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__))
 
 #endif /* __NETWORKMANAGER_DEVICE_LOGGING_H__ */
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index 2dc9d4f6..cd02cb8e 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -420,7 +420,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingMacvlan *s_macvlan;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_MACVLAN_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index a8ae3edc..eac2fb82 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -121,7 +121,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingTun *s_tun;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_TUN_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index e6e3708b..4d606e9d 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -439,7 +439,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingVlan *s_vlan;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_VLAN_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index 79942458..d308d47a 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -365,7 +365,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingVxlan *s_vxlan;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_VXLAN_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index cdd3f7cb..529b3cd8 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -70,13 +70,6 @@ _LOG_DECLARE_SELF (NMDevice);
 
 #include "nmdbus-device.h"
 
-static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
-static gboolean ip_config_valid (NMDeviceState state);
-static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason);
-static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason);
-static void nm_device_start_ip_check (NMDevice *self);
-static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink);
-
 G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_DEVICE_GET_PRIVATE(o) ((o)->priv)
@@ -414,6 +407,12 @@ static void _set_state_full (NMDevice *self,
 
 static gboolean queued_ip4_config_change (gpointer user_data);
 static gboolean queued_ip6_config_change (gpointer user_data);
+static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
+static gboolean ip_config_valid (NMDeviceState state);
+static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason);
+static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason);
+static void nm_device_start_ip_check (NMDevice *self);
+static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink);
 
 /***********************************************************/
 
@@ -1236,8 +1235,7 @@ update_dynamic_ip_setup (NMDevice *self)
 		nm_lldp_listener_stop (priv->lldp_listener);
 		addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
 
-		if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
-		                             nm_device_get_iface (self), addr, addr_length, &error)) {
+		if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) {
 			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s",
 			       priv->lldp_listener, error->message);
 			g_clear_error (&error);
@@ -2883,7 +2881,7 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 		                                               &error))
 		{
 			_LOGE (LOGD_DEVICE, "master device '%s' failed to update slave connection: %s",
-			       nm_device_get_iface (master), error ? error->message : "(unknown error)");
+			       nm_device_get_iface (master), error->message);
 			g_error_free (error);
 			g_object_unref (connection);
 			return NULL;
@@ -3164,7 +3162,7 @@ recheck_available (gpointer user_data)
 	}
 
 	if (new_state > NM_DEVICE_STATE_UNKNOWN) {
-		_LOGD (LOGD_DEVICE, "device is %savailable, %s %s",
+		_LOGD (LOGD_DEVICE, "is %savailable, %s %s",
 			   now_available ? "" : "not ",
 			   new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to",
 			   state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state));
@@ -3565,8 +3563,7 @@ activate_stage2_device_config (NMDevice *self)
 
 		addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
 
-		if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self),
-		                            nm_device_get_iface (self), addr, addr_length, &error))
+		if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error))
 			_LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
 		else {
 			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s",
@@ -4675,10 +4672,7 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres
 			}
 		}
 		nm_platform_ip4_address_set_addr (address, start + count, 24);
-
-		g_hash_table_insert (shared_ips,
-		                     GUINT_TO_POINTER (address->address),
-		                     GUINT_TO_POINTER (TRUE));
+		g_hash_table_add (shared_ips, GUINT_TO_POINTER (address->address));
 	}
 
 	return TRUE;
@@ -5852,7 +5846,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection));
 	g_assert (s_ip6);
 
-	priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self),
+	priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET,
+	                                 nm_device_get_ip_ifindex (self),
 	                                 nm_device_get_ip_iface (self),
 	                                 nm_connection_get_uuid (connection),
 	                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
@@ -6631,7 +6626,7 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 
 	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &error)) {
 		_LOGE (LOGD_SHARING, "share: (%s) failed to start dnsmasq: %s",
-		       ip_iface, (error && error->message) ? error->message : "(unknown)");
+		       ip_iface, error->message);
 		g_error_free (error);
 		nm_act_request_set_shared (req, FALSE);
 		return FALSE;
@@ -7145,6 +7140,7 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 
 	if (priv->ip4_state != IP_NONE) {
 		g_clear_object (&priv->con_ip4_config);
+		g_clear_object (&priv->ext_ip4_config);
 		priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
 		nm_ip4_config_merge_setting (priv->con_ip4_config,
 		                             s_ip4_new,
@@ -7175,6 +7171,7 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 
 	if (priv->ip6_state != IP_NONE) {
 		g_clear_object (&priv->con_ip6_config);
+		g_clear_object (&priv->ext_ip6_config);
 		priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self));
 		nm_ip6_config_merge_setting (priv->con_ip6_config,
 		                             s_ip6_new,
@@ -7374,7 +7371,10 @@ impl_device_reapply (NMDevice *self,
 
 	if (settings && g_variant_n_children (settings)) {
 		/* New settings specified inline. */
-		connection = nm_simple_connection_new_from_dbus (settings, &error);
+		connection = _nm_simple_connection_new_from_dbus (settings,
+		                                                    NM_SETTING_PARSE_FLAGS_STRICT
+		                                                  | NM_SETTING_PARSE_FLAGS_NORMALIZE,
+		                                                  &error);
 		if (!connection) {
 			g_prefix_error (&error, "The settings specified are invalid: ");
 			nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
@@ -7740,7 +7740,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req)
 
 	must_queue = _carrier_wait_check_act_request_must_queue (self, req);
 
-	if (!priv->act_request && !must_queue) {
+	if (!priv->act_request && !must_queue && nm_device_is_real (self)) {
 		/* Just activate immediately */
 		if (!_device_activate (self, req))
 			g_assert_not_reached ();
@@ -7926,9 +7926,10 @@ _replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new)
 {
 	GSList *old_link;
 
-	/* Below, assert that we have an @old instance to replace and that
-	 * @new is not yet tracked. But still, behave correctly in any
-	 * case. */
+	/* Below, assert that @new is not yet tracked, but still behave
+	 * correctly in any case. Don't complain for missing @old since
+	 * it could have been removed when the parent device became
+	 * unmanaged. */
 
 	if (   old
 	    && (old_link = g_slist_find (*plist, old))) {
@@ -7936,7 +7937,7 @@ _replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new)
 			if (new)
 				old_link->data = g_object_ref (new);
 			else
-				*plist = g_slist_remove_link (*plist, old_link);
+				*plist = g_slist_delete_link (*plist, old_link);
 			g_object_unref (old);
 		}
 		return TRUE;
@@ -7947,12 +7948,9 @@ _replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new)
 			*plist = g_slist_append (*plist, g_object_ref (new));
 		else
 			g_return_val_if_reached (TRUE);
-		g_return_val_if_fail (!old, TRUE);
 		return TRUE;
 	}
 
-	/* return FALSE if both @old and @new are unset. */
-	g_return_val_if_fail (!old, FALSE);
 	return FALSE;
 }
 
@@ -8434,7 +8432,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	_LOGD (LOGD_HW, "bringing up device.");
+	_LOGD (LOGD_HW, "bringing up device");
 
 	if (NM_DEVICE_GET_CLASS (self)->bring_up) {
 		if (!NM_DEVICE_GET_CLASS (self)->bring_up (self, no_firmware))
@@ -8508,7 +8506,7 @@ nm_device_take_down (NMDevice *self, gboolean block)
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	_LOGD (LOGD_HW, "taking down device.");
+	_LOGD (LOGD_HW, "taking down device");
 
 	if (NM_DEVICE_GET_CLASS (self)->take_down) {
 		if (!NM_DEVICE_GET_CLASS (self)->take_down (self))
@@ -9668,14 +9666,13 @@ nm_device_recheck_available_connections (NMDevice *self)
 			connection = NM_CONNECTION (iter->data);
 
 			if (nm_device_check_connection_available (self,
-				                                  connection,
-				                                  NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
-				                                  NULL)) {
+			                                          connection,
+			                                          NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
+			                                          NULL)) {
 				if (available_connections_add (self, connection))
 					changed = TRUE;
-			} else {
-				if (prune_list && g_hash_table_remove (prune_list, connection))
-					changed = TRUE;
+				if (prune_list)
+					g_hash_table_remove (prune_list, connection);
 			}
 		}
 
@@ -9697,39 +9694,55 @@ nm_device_recheck_available_connections (NMDevice *self)
 }
 
 /**
- * nm_device_get_available_connections:
+ * nm_device_get_best_connection:
  * @self: the #NMDevice
  * @specific_object: a specific object path if any
+ * @error: reason why no connection was returned
  *
- * Returns a list of connections available to activate on the device, taking
- * into account any device-specific details given by @specific_object (like
- * WiFi access point path).
+ * Returns a connection that's most suitable for user-initiated activation
+ * of a device, optionally with a given specific object.
  *
- * Returns: caller-owned #GPtrArray of #NMConnections
+ * Returns: the #NMSettingsConnection or %NULL (setting an @error)
  */
-GPtrArray *
-nm_device_get_available_connections (NMDevice *self, const char *specific_object)
+NMSettingsConnection *
+nm_device_get_best_connection (NMDevice *self,
+                               const char *specific_object,
+                               GError **error)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMSettingsConnection *connection = NULL;
+	NMSettingsConnection *candidate;
+	guint64 best_timestamp = 0;
 	GHashTableIter iter;
-	guint num_available;
-	NMConnection *connection = NULL;
-	GPtrArray *array = NULL;
 
-	num_available = g_hash_table_size (priv->available_connections);
-	if (num_available > 0) {
-		array = g_ptr_array_sized_new (num_available);
-		g_hash_table_iter_init (&iter, priv->available_connections);
-		while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) {
-			/* If a specific object is given, only include connections that are
-			 * compatible with it.
-			 */
-			if (   !specific_object /* << Optimization: we know that the connection is available without @specific_object.  */
-			    || nm_device_check_connection_available (self, connection, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, specific_object))
-				g_ptr_array_add (array, connection);
+	g_hash_table_iter_init (&iter, priv->available_connections);
+	while (g_hash_table_iter_next (&iter, (gpointer) &candidate, NULL)) {
+		guint64 candidate_timestamp = 0;
+
+		/* If a specific object is given, only include connections that are
+		 * compatible with it.
+		 */
+		if (    specific_object /* << Optimization: we know that the connection is available without @specific_object.  */
+		    && !nm_device_check_connection_available (self,
+		                                              NM_CONNECTION (candidate),
+		                                              _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
+		                                              specific_object))
+			continue;
+
+		nm_settings_connection_get_timestamp (candidate, &candidate_timestamp);
+		if (!connection || (candidate_timestamp > best_timestamp)) {
+			connection = candidate;
+			best_timestamp = candidate_timestamp;
 		}
 	}
-	return array;
+
+	if (!connection) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+		             "The device '%s' has no connections available for activation.",
+		              nm_device_get_iface (self));
+	}
+
+	return connection;
 }
 
 static void
@@ -9838,12 +9851,15 @@ nm_device_add_pending_action (NMDevice *self, const char *action, gboolean asser
 gboolean
 nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean assert_is_pending)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
 	GSList *iter, *next;
 	guint count = 0;
 
+	g_return_val_if_fail (self, FALSE);
 	g_return_val_if_fail (action, FALSE);
 
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
 	for (iter = priv->pending_actions; iter; iter = next) {
 		next = iter->next;
 		if (!strcmp (action, iter->data)) {
@@ -10352,7 +10368,7 @@ _set_state_full (NMDevice *self,
 	if (   (priv->state == state)
 	    && (   state != NM_DEVICE_STATE_UNAVAILABLE
 	        || !priv->firmware_missing)) {
-		_LOGD (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]%s",
+		_LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s') [%d %d %d]%s",
 		       state_to_string (old_state),
 		       state_to_string (state),
 		       reason_to_string (reason),
@@ -10363,7 +10379,7 @@ _set_state_full (NMDevice *self,
 		return;
 	}
 
-	_LOGI (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]",
+	_LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s') [%d %d %d]",
 	       state_to_string (old_state),
 	       state_to_string (state),
 	       reason_to_string (reason),
@@ -10389,7 +10405,8 @@ _set_state_full (NMDevice *self,
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
 		if (available_connections_del_all (self))
 			available_connections_notify (self);
-		_clear_queued_act_request (priv);
+		if (old_state > NM_DEVICE_STATE_UNAVAILABLE)
+			_clear_queued_act_request (priv);
 	}
 
 	/* Update the available connections list when a device first becomes available */
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 9b9edda4..bd657e68 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -529,8 +529,9 @@ gboolean nm_device_add_pending_action    (NMDevice *device, const char *action,
 gboolean nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending);
 gboolean nm_device_has_pending_action    (NMDevice *device);
 
-GPtrArray *nm_device_get_available_connections (NMDevice *device,
-                                                const char *specific_object);
+NMSettingsConnection *nm_device_get_best_connection (NMDevice *device,
+                                                     const char *specific_object,
+                                                     GError **error);
 
 gboolean   nm_device_check_connection_available (NMDevice *device,
                                                  NMConnection *connection,
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
index 81827817..e678dc67 100644
--- a/src/devices/nm-lldp-listener.c
+++ b/src/devices/nm-lldp-listener.c
@@ -20,33 +20,71 @@
 
 #include "nm-default.h"
 
+#include "nm-lldp-listener.h"
+
 #include <net/ethernet.h>
+#include <errno.h>
 
-#include "sd-lldp.h"
-#include "lldp.h"
-#include "nm-lldp-listener.h"
 #include "nm-platform.h"
 #include "nm-utils.h"
 
+#include "sd-lldp.h"
+
+#include "nm-sd-adapt.h"
+#include "lldp.h"
+
 #define MAX_NEIGHBORS         4096
-#define MIN_UPDATE_INTERVAL   2
+#define MIN_UPDATE_INTERVAL_NS (2 * NM_UTILS_NS_PER_SECOND)
+
+#define LLDP_MAC_NEAREST_BRIDGE          ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }))
+#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }))
+#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE ((const struct ether_addr *) ((uint8_t[ETH_ALEN]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }))
+
+typedef enum {
+	LLDP_ATTR_TYPE_NONE,
+	LLDP_ATTR_TYPE_UINT32,
+	LLDP_ATTR_TYPE_STRING,
+} LldpAttrType;
+
+typedef enum {
+	/* the order of the enum values determines the order of the fields in
+	 * the variant. */
+	LLDP_ATTR_ID_PORT_DESCRIPTION,
+	LLDP_ATTR_ID_SYSTEM_NAME,
+	LLDP_ATTR_ID_SYSTEM_DESCRIPTION,
+	LLDP_ATTR_ID_SYSTEM_CAPABILITIES,
+	LLDP_ATTR_ID_IEEE_802_1_PVID,
+	LLDP_ATTR_ID_IEEE_802_1_PPVID,
+	LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,
+	LLDP_ATTR_ID_IEEE_802_1_VID,
+	LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,
+	_LLDP_PROP_ID_COUNT,
+} LldpAttrId;
+
+typedef struct {
+	LldpAttrType attr_type;
+	union {
+		guint32 v_uint32;
+		char *v_string;
+	};
+} LldpAttrData;
 
 typedef struct {
 	char         *iface;
 	int           ifindex;
 	sd_lldp      *lldp_handle;
 	GHashTable   *lldp_neighbors;
-	guint         timer;
-	guint         num_pending_events;
+
+	/* the timestamp in nsec until which we delay updates. */
+	gint64        ratelimit_next;
+	guint         ratelimit_id;
+
 	GVariant     *variant;
 } NMLldpListenerPrivate;
 
-enum {
-	PROP_0,
+NM_GOBJECT_PROPERTIES_DEFINE (NMLldpListener,
 	PROP_NEIGHBORS,
-
-	LAST_PROP
-};
+);
 
 G_DEFINE_TYPE (NMLldpListener, nm_lldp_listener, G_TYPE_OBJECT)
 
@@ -58,12 +96,14 @@ typedef struct {
 	char *chassis_id;
 	char *port_id;
 
-	int dest;
+	struct ether_addr destination_address;
+
+	bool valid:1;
 
-	GHashTable *tlvs;
-} LLDPNeighbor;
+	LldpAttrData attrs[_LLDP_PROP_ID_COUNT];
 
-static void process_lldp_neighbors (NMLldpListener *self);
+	GVariant *variant;
+} LldpNeighbor;
 
 /*****************************************************************************/
 
@@ -89,43 +129,139 @@ static void process_lldp_neighbors (NMLldpListener *self);
         } \
     } G_STMT_END \
 
+#define LOG_NEIGH_FMT        "CHASSIS=%s%s%s PORT=%s%s%s"
+#define LOG_NEIGH_ARG(neigh) NM_PRINT_FMT_QUOTE_STRING ((neigh)->chassis_id), NM_PRINT_FMT_QUOTE_STRING ((neigh)->port_id)
+
 /*****************************************************************************/
 
+static gboolean
+ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2)
+{
+	nm_assert (a1);
+	nm_assert (a2);
+
+	G_STATIC_ASSERT_EXPR (sizeof (*a1) == ETH_ALEN);
+	return memcmp (a1, a2, ETH_ALEN) == 0;
+}
+
+static guint32
+_access_uint8 (const void *data)
+{
+	return *((const guint8 *) data);
+}
+
+static guint32
+_access_uint16 (const void *data)
+{
+	guint16 v;
+
+	memcpy (&v, data, sizeof (v));
+	return ntohs (v);
+}
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_lldp_attr_id_to_name, LldpAttrId,
+	NM_UTILS_LOOKUP_DEFAULT_WARN (NULL),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_PORT_DESCRIPTION,        NM_LLDP_ATTR_PORT_DESCRIPTION),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_NAME,             NM_LLDP_ATTR_SYSTEM_NAME),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION,      NM_LLDP_ATTR_SYSTEM_DESCRIPTION),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES,     NM_LLDP_ATTR_SYSTEM_CAPABILITIES),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID,         NM_LLDP_ATTR_IEEE_802_1_PVID),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID,        NM_LLDP_ATTR_IEEE_802_1_PPVID),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,  NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID,          NM_LLDP_ATTR_IEEE_802_1_VID),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,    NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_PROP_ID_COUNT),
+);
+
+_NM_UTILS_LOOKUP_DEFINE (static, _lldp_attr_id_to_type, LldpAttrId, LldpAttrType,
+	NM_UTILS_LOOKUP_DEFAULT_WARN (LLDP_ATTR_TYPE_NONE),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_PORT_DESCRIPTION,            LLDP_ATTR_TYPE_STRING),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_NAME,                 LLDP_ATTR_TYPE_STRING),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION,          LLDP_ATTR_TYPE_STRING),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES,         LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID,             LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID,            LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,      LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID,              LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,        LLDP_ATTR_TYPE_STRING),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_PROP_ID_COUNT),
+);
+
 static void
-gvalue_destroy (gpointer data)
+_lldp_attr_set_str (LldpAttrData *pdata, LldpAttrId attr_id, const char *v_string)
 {
-	GValue *value = (GValue *) data;
+	nm_assert (pdata);
+	nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_STRING);
+
+	pdata = &pdata[attr_id];
 
-	g_value_unset (value);
-	g_slice_free (GValue, value);
+	/* we ignore duplicate fields silently. */
+	if (pdata->attr_type != LLDP_ATTR_TYPE_NONE)
+		return;
+	pdata->attr_type = LLDP_ATTR_TYPE_STRING;
+	pdata->v_string = g_strdup (v_string ?: "");
 }
 
-static GValue *
-gvalue_new_nstr (const char *str, guint16 len)
+static void
+_lldp_attr_set_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, const void *str, gsize len)
 {
-	GValue *value;
+	const char *s = str;
+	const char *tmp;
+	gsize len0 = len;
+	gs_free char *str_free = NULL;
+
+	nm_assert (pdata);
+	nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_STRING);
+
+	pdata = &pdata[attr_id];
+
+	/* we ignore duplicate fields silently. */
+	if (pdata->attr_type != LLDP_ATTR_TYPE_NONE)
+		return;
+
+	pdata->attr_type = LLDP_ATTR_TYPE_STRING;
 
-	value = g_slice_new0 (GValue);
-	g_value_init (value, G_TYPE_STRING);
-	g_value_take_string (value, strndup (str, len));
-	return value;
+	/* truncate at first NUL, including removing trailing NULs*/
+	tmp = memchr (s, '\0', len);
+	if (tmp)
+		len = tmp - s;
+
+	if (!len) {
+		pdata->v_string = g_strdup ("");
+		return;
+	}
+
+	if (len0 <= len || s[len] != '\0') {
+		/* hmpf, g_strescape needs a trailing NUL. Need to clone */
+		s = str_free = g_strndup (s, len);
+	}
+
+	pdata->v_string = g_strescape (s, NULL);
 }
 
-static GValue *
-gvalue_new_uint (guint val)
+static void
+_lldp_attr_set_uint32 (LldpAttrData *pdata, LldpAttrId attr_id, guint32 v_uint32)
 {
-	GValue *value;
+	nm_assert (pdata);
+	nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_UINT32);
+
+	pdata = &pdata[attr_id];
 
-	value = g_slice_new0 (GValue);
-	g_value_init (value, G_TYPE_UINT);
-	g_value_set_uint (value, val);
-	return value;
+	/* we ignore duplicate fields silently. */
+	if (pdata->attr_type != LLDP_ATTR_TYPE_NONE)
+		return;
+	pdata->attr_type = LLDP_ATTR_TYPE_UINT32;
+	pdata->v_uint32 = v_uint32;
 }
 
+/*****************************************************************************/
+
 static guint
 lldp_neighbor_id_hash (gconstpointer ptr)
 {
-	const LLDPNeighbor *neigh = ptr;
+	const LldpNeighbor *neigh = ptr;
 	guint hash;
 
 	hash =   23423423u  + ((guint) (neigh->chassis_id ? g_str_hash (neigh->chassis_id) : 12321u));
@@ -135,303 +271,453 @@ lldp_neighbor_id_hash (gconstpointer ptr)
 	return hash;
 }
 
+static int
+lldp_neighbor_id_cmp (gconstpointer a, gconstpointer b)
+{
+	const LldpNeighbor *x = a, *y = b;
+	int c;
+
+	if (x->chassis_id_type != y->chassis_id_type)
+		return x->chassis_id_type < y->chassis_id_type ? -1 : 1;
+	if (x->port_id_type != y->port_id_type)
+		return x->port_id_type < y->port_id_type ? -1 : 1;
+	c = g_strcmp0 (x->chassis_id, y->chassis_id);
+	if (c == 0)
+		c = g_strcmp0 (x->port_id, y->port_id);
+	return c < 0 ? -1 : (c > 0 ? 1 : 0);
+}
+
 static gboolean
 lldp_neighbor_id_equal (gconstpointer a, gconstpointer b)
 {
-	const LLDPNeighbor *x = a, *y = b;
-
-	return x->chassis_id_type == y->chassis_id_type &&
-	       x->port_id_type == y->port_id_type &&
-	       !g_strcmp0 (x->chassis_id, y->chassis_id) &&
-	       !g_strcmp0 (x->port_id, y->port_id);
+	return lldp_neighbor_id_cmp (a, b) == 0;
 }
 
 static void
-lldp_neighbor_free (LLDPNeighbor *neighbor)
+lldp_neighbor_free (LldpNeighbor *neighbor)
 {
+	LldpAttrId attr_id;
+
 	if (neighbor) {
 		g_free (neighbor->chassis_id);
 		g_free (neighbor->port_id);
-		g_hash_table_unref (neighbor->tlvs);
-		g_slice_free (LLDPNeighbor, neighbor);
+		for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) {
+			if (neighbor->attrs[attr_id].attr_type == LLDP_ATTR_TYPE_STRING)
+				g_free (neighbor->attrs[attr_id].v_string);
+		}
+		g_clear_pointer (&neighbor->variant, g_variant_unref);
+		g_slice_free (LldpNeighbor, neighbor);
 	}
 }
 
 static void
-lldp_neighbor_freep (LLDPNeighbor **ptr)
+lldp_neighbor_freep (LldpNeighbor **ptr)
 {
 	lldp_neighbor_free (*ptr);
 }
 
 static gboolean
-lldp_neighbor_equal (LLDPNeighbor *a, LLDPNeighbor *b)
+lldp_neighbor_equal (LldpNeighbor *a, LldpNeighbor *b)
 {
-	GHashTableIter iter;
-	gpointer k, v;
+	LldpAttrId attr_id;
 
-	g_return_val_if_fail (a && a->tlvs, FALSE);
-	g_return_val_if_fail (b && b->tlvs, FALSE);
+	nm_assert (a);
+	nm_assert (b);
 
 	if (   a->chassis_id_type != b->chassis_id_type
 	    || a->port_id_type != b->port_id_type
-	    || a->dest != b->dest
-	    || g_strcmp0 (a->chassis_id, b->chassis_id)
-	    || g_strcmp0 (a->port_id, b->port_id))
-		return FALSE;
-
-	if (g_hash_table_size (a->tlvs) != g_hash_table_size (b->tlvs))
+	    || ether_addr_equal (&a->destination_address, &b->destination_address)
+	    || !nm_streq0 (a->chassis_id, b->chassis_id)
+	    || !nm_streq0 (a->port_id, b->port_id))
 		return FALSE;
 
-	g_hash_table_iter_init (&iter, a->tlvs);
-	while (g_hash_table_iter_next (&iter, &k, &v)) {
-		GValue *value_a, *value_b;
-
-		value_a = v;
-		value_b = g_hash_table_lookup (b->tlvs, k);
-
-		if (!value_b)
+	for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) {
+		if (a->attrs[attr_id].attr_type != b->attrs[attr_id].attr_type)
 			return FALSE;
-
-		g_return_val_if_fail (G_VALUE_TYPE (value_a) == G_VALUE_TYPE (value_b), FALSE);
-
-		if (G_VALUE_HOLDS_STRING (value_a)) {
-			if (g_strcmp0 (g_value_get_string (value_a), g_value_get_string (value_b)))
+		switch (a->attrs[attr_id].attr_type) {
+		case LLDP_ATTR_TYPE_UINT32:
+			if (a->attrs[attr_id].v_uint32 != b->attrs[attr_id].v_uint32)
 				return FALSE;
-		} else if (G_VALUE_HOLDS_UINT (value_a)) {
-			if (g_value_get_uint (value_a) != g_value_get_uint (value_b))
+			break;
+		case LLDP_ATTR_TYPE_STRING:
+			if (!nm_streq (a->attrs[attr_id].v_string, b->attrs[attr_id].v_string))
 				return FALSE;
-		} else
-			g_return_val_if_reached (FALSE);
+			break;
+		default:
+			nm_assert (a->attrs[attr_id].attr_type == LLDP_ATTR_TYPE_NONE);
+			break;
+		}
 	}
 
 	return TRUE;
 }
 
-static gboolean
-lldp_hash_table_equal (GHashTable *a, GHashTable *b)
+static LldpNeighbor *
+lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error)
 {
-	GHashTableIter iter;
-	gpointer val;
-
-	g_return_val_if_fail (a, FALSE);
-	g_return_val_if_fail (b, FALSE);
+	nm_auto (lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
+	uint8_t chassis_id_type, port_id_type;
+	uint16_t data16;
+	uint8_t *data8;
+	const void *chassis_id, *port_id;
+	gsize chassis_id_len, port_id_len, len;
+	const char *str;
+	int r;
+
+	r = sd_lldp_neighbor_get_chassis_id (neighbor_sd, &chassis_id_type,
+	                                     &chassis_id, &chassis_id_len);
+	if (r < 0) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "failed reading chassis-id: %s", g_strerror (-r));
+		return NULL;
+	}
+	if (chassis_id_len < 1) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "empty chassis-id");
+		return NULL;
+	}
 
-	if (g_hash_table_size (a) != g_hash_table_size (b))
-		return FALSE;
+	r = sd_lldp_neighbor_get_port_id (neighbor_sd, &port_id_type,
+	                                  &port_id, &port_id_len);
+	if (r < 0) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "failed reading port-id: %s", g_strerror (-r));
+		return NULL;
+	}
+	if (port_id_len < 1) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "empty port-id");
+		return NULL;
+	}
 
-	g_hash_table_iter_init (&iter, a);
-	while (g_hash_table_iter_next (&iter, NULL, &val)) {
-		LLDPNeighbor *neigh_a, *neigh_b;
+	neigh = g_slice_new0 (LldpNeighbor);
+	neigh->chassis_id_type = chassis_id_type;
+	neigh->port_id_type = port_id_type;
 
-		neigh_a = val;
-		neigh_b = g_hash_table_lookup (b, val);
+	r = sd_lldp_neighbor_get_destination_address (neighbor_sd, &neigh->destination_address);
+	if (r < 0) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "failed getting destination address: %s", g_strerror (-r));
+		goto out;
+	}
 
-		if (!neigh_b)
-			return FALSE;
+	switch (chassis_id_type) {
+	case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
+	case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
+	case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
+	case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
+		neigh->chassis_id = g_strndup ((const char *) chassis_id, chassis_id_len);
+		break;
+	case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+		neigh->chassis_id = nm_utils_hwaddr_ntoa (chassis_id, chassis_id_len);
+		break;
+	default:
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "unsupported chassis-id type %d", chassis_id_type);
+		goto out;
+	}
 
-		if (!lldp_neighbor_equal (neigh_a, neigh_b))
-			return FALSE;
+	switch (port_id_type) {
+	case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+	case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+	case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+	case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+		neigh->port_id = strndup ((char *) port_id, port_id_len);
+		break;
+	case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+		neigh->port_id = nm_utils_hwaddr_ntoa (port_id, port_id_len);
+		break;
+	default:
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "unsupported port-id type %d", port_id_type);
+		goto out;
 	}
 
-	return TRUE;
-}
+	if (sd_lldp_neighbor_get_port_description (neighbor_sd, &str) == 0)
+		_lldp_attr_set_str (neigh->attrs, LLDP_ATTR_ID_PORT_DESCRIPTION, str);
 
-static gboolean
-lldp_timeout (gpointer user_data)
-{
-	NMLldpListener *self = user_data;
-	NMLldpListenerPrivate *priv;
+	if (sd_lldp_neighbor_get_system_name (neighbor_sd, &str) == 0)
+		_lldp_attr_set_str (neigh->attrs, LLDP_ATTR_ID_SYSTEM_NAME, str);
 
-	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), G_SOURCE_REMOVE);
+	if (sd_lldp_neighbor_get_system_description (neighbor_sd, &str) == 0)
+		_lldp_attr_set_str (neigh->attrs, LLDP_ATTR_ID_SYSTEM_DESCRIPTION, str);
 
-	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+	if (sd_lldp_neighbor_get_system_capabilities (neighbor_sd, &data16) == 0)
+		_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_SYSTEM_CAPABILITIES, data16);
 
-	priv->timer = 0;
+	r = sd_lldp_neighbor_tlv_rewind (neighbor_sd);
+	if (r < 0) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "failed reading tlv (rewind): %s", g_strerror (-r));
+		goto out;
+	}
+	do {
+		guint8 oui[3];
+		guint8 subtype;
+
+		r = sd_lldp_neighbor_tlv_get_oui (neighbor_sd, oui, &subtype);
+		if (r < 0) {
+			if (r == -ENXIO)
+				continue;
+			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+			             "failed reading tlv: %s", g_strerror (-r));
+			goto out;
+		}
 
-	if (priv->num_pending_events)
-		process_lldp_neighbors (self);
+		if (!(   memcmp (oui, LLDP_OUI_802_1, sizeof (oui)) == 0
+		      && NM_IN_SET (subtype,
+		                    LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID,
+		                    LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID,
+		                    LLDP_OUI_802_1_SUBTYPE_VLAN_NAME)))
+			continue;
+
+		if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0)
+			continue;
+
+		/* skip over leading TLV, OUI and subtype */
+#ifdef WITH_MORE_ASSERTS
+		{
+			guint8 check_hdr[] = {
+				0xfe | (((len - 2) >> 8) & 0x01), ((len - 2) & 0xFF),
+				oui[0], oui[1], oui[2],
+				subtype
+			};
+
+			nm_assert (len > 2 + 3 +1);
+			nm_assert (memcmp (data8, check_hdr, sizeof check_hdr) == 0);
+		}
+#endif
+		if (len <= 6)
+			continue;
+		data8 += 6;
+		len -= 6;
+
+		/*if (memcmp (oui, LLDP_OUI_802_1, sizeof (oui)) == 0)*/
+		{
+			switch (subtype) {
+			case LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID:
+				if (len != 2)
+					continue;
+				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PVID,
+				                       _access_uint16 (data8));
+				break;
+			case LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID:
+				if (len != 3)
+					continue;
+				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,
+				                       _access_uint8 (&data8[0]));
+				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PPVID,
+				                       _access_uint16 (&data8[1]));
+				break;
+			case LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: {
+				int l;
+
+				if (len <= 3)
+					continue;
+
+				l = data8[2];
+				if (len != 3 + l)
+					continue;
+
+				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VID,
+				                       _access_uint16 (&data8[0]));
+				_lldp_attr_set_str_ptr (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,
+				                        &data8[3], len);
+				break;
+			}
+			default:
+				g_assert_not_reached ();
+			}
+		}
+	} while (sd_lldp_neighbor_tlv_next (neighbor_sd) > 0);
 
-	return G_SOURCE_REMOVE;
+	neigh->valid = TRUE;
+
+out:
+	return nm_unauto (&neigh);
 }
 
-static void
-process_lldp_neighbors (NMLldpListener *self)
+static GVariant *
+lldp_neighbor_to_variant (LldpNeighbor *neigh)
 {
-	NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
-	nm_auto_free sd_lldp_packet **packets = NULL;
-	GHashTable *hash;
-	int num, i;
-
-	g_return_if_fail (priv->lldp_handle);
-
-	num = sd_lldp_get_packets (priv->lldp_handle, &packets);
-	if (num < 0) {
-		_LOGD ("process: error %d retrieving neighbor packets for %s",
-		        num, priv->iface);
-		return;
+	GVariantBuilder builder;
+	const char *dest_str;
+	LldpAttrId attr_id;
+
+	if (neigh->variant)
+		return neigh->variant;
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+	g_variant_builder_add (&builder, "{sv}",
+	                       NM_LLDP_ATTR_CHASSIS_ID_TYPE,
+	                       g_variant_new_uint32 (neigh->chassis_id_type));
+	g_variant_builder_add (&builder, "{sv}",
+	                       NM_LLDP_ATTR_CHASSIS_ID,
+	                       g_variant_new_string (neigh->chassis_id));
+	g_variant_builder_add (&builder, "{sv}",
+	                       NM_LLDP_ATTR_PORT_ID_TYPE,
+	                       g_variant_new_uint32 (neigh->port_id_type));
+	g_variant_builder_add (&builder, "{sv}",
+	                       NM_LLDP_ATTR_PORT_ID,
+	                       g_variant_new_string (neigh->port_id));
+
+	if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_BRIDGE))
+		dest_str = NM_LLDP_DEST_NEAREST_BRIDGE;
+	else if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE))
+		dest_str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
+	else if (ether_addr_equal (&neigh->destination_address, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE))
+		dest_str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
+	else
+		dest_str = NULL;
+	if (dest_str) {
+		g_variant_builder_add (&builder, "{sv}",
+		                       NM_LLDP_ATTR_DESTINATION,
+		                       g_variant_new_string (dest_str));
 	}
 
-	hash = g_hash_table_new_full (lldp_neighbor_id_hash, lldp_neighbor_id_equal,
-	                              (GDestroyNotify) lldp_neighbor_free, NULL);
-
-	for (i = 0; packets && i < num; i++) {
-		nm_auto (lldp_neighbor_freep) LLDPNeighbor *neigh = NULL;
-		uint8_t chassis_id_type, port_id_type, *chassis_id, *port_id, data8;
-		uint16_t chassis_id_len, port_id_len, len, data16;
-		GValue *value;
-		char *str;
-		int r;
-
-		if (i >= MAX_NEIGHBORS)
-			goto next_packet;
-
-		r = sd_lldp_packet_read_chassis_id (packets[i], &chassis_id_type,
-		                                    &chassis_id, &chassis_id_len);
-		if (r < 0)
-			goto next_packet;
-
-		r = sd_lldp_packet_read_port_id (packets[i], &port_id_type,
-		                                 &port_id, &port_id_len);
-		if (r < 0)
-			goto next_packet;
-
-		neigh = g_slice_new0 (LLDPNeighbor);
-		neigh->tlvs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gvalue_destroy);
-		neigh->chassis_id_type = chassis_id_type;
-		neigh->port_id_type = port_id_type;
-		sd_lldp_packet_get_destination_type (packets[i], &neigh->dest);
-
-		if (chassis_id_len < 1)
-			goto next_packet;
-
-		switch (chassis_id_type) {
-		case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
-		case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
-		case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
-		case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
-			neigh->chassis_id = g_strndup ((const char *) chassis_id, chassis_id_len);
-			break;
-		case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-			neigh->chassis_id = nm_utils_hwaddr_ntoa (chassis_id, chassis_id_len);
-			break;
-		default:
-			_LOGD ("process: unsupported chassis ID type %d", chassis_id_type);
-			goto next_packet;
-		}
-
-		if (port_id_len < 1)
-			goto next_packet;
+	for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) {
+		const LldpAttrData *data = &neigh->attrs[attr_id];
 
-		switch (port_id_type) {
-		case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
-		case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
-		case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-		case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
-			neigh->port_id = strndup ((char *) port_id, port_id_len);
+		nm_assert (NM_IN_SET (data->attr_type, _lldp_attr_id_to_type (attr_id), LLDP_ATTR_TYPE_NONE));
+		switch (data->attr_type) {
+		case LLDP_ATTR_TYPE_UINT32:
+			g_variant_builder_add (&builder, "{sv}",
+			                       _lldp_attr_id_to_name (attr_id),
+			                       g_variant_new_uint32 (data->v_uint32));
 			break;
-		case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-			neigh->port_id = nm_utils_hwaddr_ntoa (port_id, port_id_len);
+		case LLDP_ATTR_TYPE_STRING:
+			g_variant_builder_add (&builder, "{sv}",
+			                       _lldp_attr_id_to_name (attr_id),
+			                       g_variant_new_string (data->v_string));
 			break;
 		default:
-			_LOGD ("process: unsupported port ID type %d", port_id_type);
-			goto next_packet;
-		}
-
-		if (sd_lldp_packet_read_port_description (packets[i], &str, &len) == 0) {
-			value = gvalue_new_nstr (str, len);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_PORT_DESCRIPTION, value);
-		}
-
-		if (sd_lldp_packet_read_system_name (packets[i], &str, &len) == 0) {
-			value = gvalue_new_nstr (str, len);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_NAME, value);
-		}
-
-		if (sd_lldp_packet_read_system_description (packets[i], &str, &len) == 0) {
-			value = gvalue_new_nstr (str, len);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, value);
-		}
-
-		if (sd_lldp_packet_read_system_capability (packets[i], &data16) == 0) {
-			value = gvalue_new_uint (data16);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, value);
-		}
-
-		if (sd_lldp_packet_read_port_vlan_id (packets[i], &data16) == 0) {
-			value = gvalue_new_uint (data16);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PVID, value);
+			break;
 		}
+	}
 
-		if (sd_lldp_packet_read_port_protocol_vlan_id (packets[i], &data8, &data16) == 0) {
-			value = gvalue_new_uint (data16);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID, value);
+	return (neigh->variant = g_variant_ref_sink (g_variant_builder_end (&builder)));
+}
 
-			value = gvalue_new_uint (data8);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, value);
-		}
+/*****************************************************************************/
 
-		if (sd_lldp_packet_read_vlan_name (packets[i], &data16, &str, &len) == 0) {
-			value = gvalue_new_uint (data16);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VID, value);
+static void
+data_changed_notify (NMLldpListener *self, NMLldpListenerPrivate *priv)
+{
+	nm_clear_g_variant (&priv->variant);
+	_notify (self, PROP_NEIGHBORS);
+}
 
-			value = gvalue_new_nstr (str, len);
-			g_hash_table_insert (neigh->tlvs, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, value);
-		}
+static gboolean
+data_changed_timeout (gpointer user_data)
+{
+	NMLldpListener *self = user_data;
+	NMLldpListenerPrivate *priv;
 
-		_LOGD ("process: new neigh: CHASSIS='%s' PORT='%s'",
-		        neigh->chassis_id, neigh->port_id);
+	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), G_SOURCE_REMOVE);
 
-		g_hash_table_add (hash, neigh);
-		neigh = NULL;
-next_packet:
-		sd_lldp_packet_unref (packets[i]);
-	}
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 
-	if (lldp_hash_table_equal (priv->lldp_neighbors, hash)) {
-		g_hash_table_destroy (hash);
-	} else {
-		g_hash_table_destroy (priv->lldp_neighbors);
-		priv->lldp_neighbors = hash;
-		nm_clear_g_variant (&priv->variant);
-		g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS);
-	}
+	priv->ratelimit_id = 0;
+	priv->ratelimit_next = nm_utils_get_monotonic_timestamp_ns() + MIN_UPDATE_INTERVAL_NS;
+	data_changed_notify (self, priv);
+	return G_SOURCE_REMOVE;
+}
 
-	/* Since the processing of the neighbor list is potentially
-	 * expensive when there are many neighbors, coalesce multiple
-	 * events arriving in short time.
-	 */
-	priv->timer = g_timeout_add_seconds (MIN_UPDATE_INTERVAL, lldp_timeout, self);
-	priv->num_pending_events = 0;
+static void
+data_changed_schedule (NMLldpListener *self)
+{
+	NMLldpListenerPrivate *priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
+	gint64 now;
+
+	now = nm_utils_get_monotonic_timestamp_ns ();
+	if (now >= priv->ratelimit_next) {
+		nm_clear_g_source (&priv->ratelimit_id);
+		priv->ratelimit_next = now + MIN_UPDATE_INTERVAL_NS;
+		data_changed_notify (self, priv);
+	} else if (!priv->ratelimit_id)
+		priv->ratelimit_id = g_timeout_add (NM_UTILS_NS_TO_MSEC_CEIL (priv->ratelimit_next - now), data_changed_timeout, self);
 }
 
 static void
-lldp_event_handler (sd_lldp *lldp, int event, void *userdata)
+process_lldp_neighbor (NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gboolean neighbor_valid)
 {
-	NMLldpListener *self = userdata;
 	NMLldpListenerPrivate *priv;
+	nm_auto (lldp_neighbor_freep) LldpNeighbor *neigh = NULL;
+	LldpNeighbor *neigh_old;
+	gs_free_error GError *parse_error = NULL;
+	GError **p_parse_error;
+	gboolean changed = FALSE;
 
 	g_return_if_fail (NM_IS_LLDP_LISTENER (self));
 
 	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 
-	if (priv->timer > 0) {
-		priv->num_pending_events++;
+	g_return_if_fail (priv->lldp_handle);
+	g_return_if_fail (neighbor_sd);
+
+	p_parse_error = _LOGT_ENABLED () ? &parse_error : NULL;
+
+	neigh = lldp_neighbor_new (neighbor_sd, p_parse_error);
+	if (!neigh) {
+		_LOGT ("process: failed to parse neighbor: %s", parse_error->message);
+		return;
+	}
+
+	if (!neigh->valid)
+		neighbor_valid = FALSE;
+
+	neigh_old = g_hash_table_lookup (priv->lldp_neighbors, neigh);
+	if (neigh_old) {
+		if (!neighbor_valid) {
+			_LOGT ("process: %s neigh: "LOG_NEIGH_FMT"%s%s%s",
+			       "remove", LOG_NEIGH_ARG (neigh),
+			       NM_PRINT_FMT_QUOTED (parse_error, " (failed to parse: ", parse_error->message, ")", ""));
+
+			g_hash_table_remove (priv->lldp_neighbors, neigh_old);
+			changed = TRUE;
+			goto done;
+		} else if (lldp_neighbor_equal (neigh_old, neigh))
+			return;
+	} else if (!neighbor_valid) {
+		if (parse_error)
+			_LOGT ("process: failed to parse neighbor: %s", parse_error->message);
+		return;
+	}
+
+	/* ensure that we have at most MAX_NEIGHBORS entires */
+	if (   !neigh_old /* only matters in the "add" case. */
+	    && (g_hash_table_size (priv->lldp_neighbors) + 1 > MAX_NEIGHBORS)) {
+		_LOGT ("process: ignore neighbor due to overall limit of %d", MAX_NEIGHBORS);
 		return;
 	}
 
-	process_lldp_neighbors (self);
+	_LOGD ("process: %s neigh: "LOG_NEIGH_FMT,
+	        neigh_old ? "update" : "new",
+	        LOG_NEIGH_ARG (neigh));
+
+	changed = TRUE;
+	g_hash_table_add (priv->lldp_neighbors, nm_unauto (&neigh));
+
+done:
+	if (changed)
+		data_changed_schedule (self);
+}
+
+static void
+lldp_event_handler (sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata)
+{
+	process_lldp_neighbor (userdata, n, event != SD_LLDP_EVENT_REMOVED);
 }
 
 gboolean
-nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
-                        const guint8 *mac, guint mac_len, GError **error)
+nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error)
 {
 	NMLldpListenerPrivate *priv;
 	int ret;
 
 	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
 	g_return_val_if_fail (ifindex > 0, FALSE);
-	g_return_val_if_fail (iface, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
 	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
@@ -442,43 +728,38 @@ nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
 		return FALSE;
 	}
 
-	if (!mac || mac_len != ETH_ALEN) {
+	ret = sd_lldp_new (&priv->lldp_handle, ifindex);
+	if (ret < 0) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
-		                     "unsupported device");
+		                     "initialization failed");
 		return FALSE;
 	}
 
-	ret = sd_lldp_new (ifindex, iface, (struct ether_addr *) mac, &priv->lldp_handle);
-	if (ret) {
+	ret = sd_lldp_set_callback (priv->lldp_handle, lldp_event_handler, self);
+	if (ret < 0) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
-		                     "initialization failed");
-		return FALSE;
+		                     "set callback failed");
+		goto err;
 	}
 
+	priv->ifindex = ifindex;
+
 	ret = sd_lldp_attach_event (priv->lldp_handle, NULL, 0);
-	if (ret) {
+	if (ret < 0) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		                     "attach event failed");
 		goto err_free;
 	}
 
-	ret = sd_lldp_set_callback (priv->lldp_handle, lldp_event_handler, self);
-	if (ret) {
-		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
-		                     "set callback failed");
-		goto err;
-	}
-
 	ret = sd_lldp_start (priv->lldp_handle);
-	if (ret) {
+	if (ret < 0) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		                     "start failed");
 		goto err;
 	}
 
-	priv->ifindex = ifindex;
-	priv->iface = strdup (iface);
 	_LOGD ("start");
+
 	return TRUE;
 
 err:
@@ -486,6 +767,7 @@ err:
 err_free:
 	sd_lldp_unref (priv->lldp_handle);
 	priv->lldp_handle = NULL;
+	priv->ifindex = 0;
 	return FALSE;
 }
 
@@ -494,6 +776,7 @@ nm_lldp_listener_stop (NMLldpListener *self)
 {
 	NMLldpListenerPrivate *priv;
 	guint size;
+	gboolean changed = FALSE;
 
 	g_return_if_fail (NM_IS_LLDP_LISTENER (self));
 	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
@@ -503,19 +786,20 @@ nm_lldp_listener_stop (NMLldpListener *self)
 		sd_lldp_stop (priv->lldp_handle);
 		sd_lldp_detach_event (priv->lldp_handle);
 		sd_lldp_unref (priv->lldp_handle);
-		g_clear_pointer (&priv->iface, g_free);
 		priv->lldp_handle = NULL;
 
 		size = g_hash_table_size (priv->lldp_neighbors);
 		g_hash_table_remove_all (priv->lldp_neighbors);
-		if (size) {
-			nm_clear_g_variant (&priv->variant);
-			g_object_notify (G_OBJECT (self), NM_LLDP_LISTENER_NEIGHBORS);
-		}
+		if (size || priv->ratelimit_id)
+			changed = TRUE;
 	}
 
-	nm_clear_g_source (&priv->timer);
+	nm_clear_g_source (&priv->ratelimit_id);
+	priv->ratelimit_next = 0;
 	priv->ifindex = 0;
+
+	if (changed)
+		data_changed_notify (self, priv);
 }
 
 gboolean
@@ -532,78 +816,23 @@ nm_lldp_listener_is_running (NMLldpListener *self)
 GVariant *
 nm_lldp_listener_get_neighbors (NMLldpListener *self)
 {
-	GVariantBuilder array_builder, neigh_builder;
-	GHashTableIter iter;
 	NMLldpListenerPrivate *priv;
-	LLDPNeighbor *neigh;
-	char *dest_str = NULL;
+	GVariantBuilder array_builder;
+	GList *neighbors, *iter;
 
-	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
-
-	if (priv->variant)
-		goto out;
-
-	g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
-	g_hash_table_iter_init (&iter, priv->lldp_neighbors);
-
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &neigh)) {
-		GHashTableIter val_iter;
-		gpointer key, val;
-
-		g_variant_builder_init (&neigh_builder, G_VARIANT_TYPE ("a{sv}"));
-
-		g_variant_builder_add (&neigh_builder, "{sv}",
-		                       NM_LLDP_ATTR_CHASSIS_ID_TYPE,
-		                       g_variant_new_uint32 (neigh->chassis_id_type));
-		g_variant_builder_add (&neigh_builder, "{sv}",
-		                       NM_LLDP_ATTR_CHASSIS_ID,
-		                       g_variant_new_string (neigh->chassis_id));
-		g_variant_builder_add (&neigh_builder, "{sv}",
-		                       NM_LLDP_ATTR_PORT_ID_TYPE,
-		                       g_variant_new_uint32 (neigh->port_id_type));
-		g_variant_builder_add (&neigh_builder, "{sv}",
-		                       NM_LLDP_ATTR_PORT_ID,
-		                       g_variant_new_string (neigh->port_id));
-
-		switch (neigh->dest) {
-		case SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE:
-			dest_str = NM_LLDP_DEST_NEAREST_BRIDGE;
-			break;
-		case SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE:
-			dest_str = NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE;
-			break;
-		case SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE:
-			dest_str = NM_LLDP_DEST_NEAREST_CUSTOMER_BRIDGE;
-			break;
-		}
-
-		if (dest_str) {
-			g_variant_builder_add (&neigh_builder, "{sv}",
-			                       NM_LLDP_ATTR_DESTINATION,
-			                       g_variant_new_string (dest_str));
-		}
+	g_return_val_if_fail (NM_IS_LLDP_LISTENER (self), FALSE);
 
-		g_hash_table_iter_init (&val_iter, neigh->tlvs);
-		while (g_hash_table_iter_next (&val_iter, &key, &val)) {
-			GValue *item = val;
-
-			if (G_VALUE_HOLDS_STRING (item)) {
-				g_variant_builder_add (&neigh_builder, "{sv}",
-				                       key,
-				                       g_variant_new_string (g_value_get_string (item)));
-			} else if (G_VALUE_HOLDS_UINT (item)) {
-				g_variant_builder_add (&neigh_builder, "{sv}",
-				                       key,
-				                       g_variant_new_uint32 (g_value_get_uint (item)));
-			}
-		}
+	priv = NM_LLDP_LISTENER_GET_PRIVATE (self);
 
-		g_variant_builder_add (&array_builder, "a{sv}", &neigh_builder);
+	if (!priv->variant) {
+		g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
+		neighbors = g_hash_table_get_keys (priv->lldp_neighbors);
+		neighbors = g_list_sort (neighbors, lldp_neighbor_id_cmp);
+		for (iter = neighbors; iter; iter = iter->next)
+			g_variant_builder_add_value (&array_builder, lldp_neighbor_to_variant (iter->data));
+		g_list_free (neighbors);
+		priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder));
 	}
-
-	priv->variant = g_variant_ref_sink (g_variant_builder_end (&array_builder));
-
-out:
 	return priv->variant;
 }
 
@@ -676,12 +905,13 @@ nm_lldp_listener_class_init (NMLldpListenerClass *klass)
 	object_class->finalize = finalize;
 	object_class->get_property = get_property;
 
-	g_object_class_install_property
-		(object_class, PROP_NEIGHBORS,
-		 g_param_spec_variant (NM_LLDP_LISTENER_NEIGHBORS, "", "",
-		                       G_VARIANT_TYPE ("aa{sv}"),
-		                       NULL,
-		                       G_PARAM_READABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NEIGHBORS] =
+	    g_param_spec_variant (NM_LLDP_LISTENER_NEIGHBORS, "", "",
+	                          G_VARIANT_TYPE ("aa{sv}"),
+	                          NULL,
+	                          G_PARAM_READABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
 
diff --git a/src/devices/nm-lldp-listener.h b/src/devices/nm-lldp-listener.h
index 10c43e7c..df02fd63 100644
--- a/src/devices/nm-lldp-listener.h
+++ b/src/devices/nm-lldp-listener.h
@@ -42,8 +42,7 @@ typedef struct {
 
 GType nm_lldp_listener_get_type (void);
 NMLldpListener *nm_lldp_listener_new (void);
-gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, const char *iface,
-                                 const guint8 *mac, guint mac_len, GError **error);
+gboolean nm_lldp_listener_start (NMLldpListener *self, int ifindex, GError **error);
 void nm_lldp_listener_stop (NMLldpListener *self);
 gboolean nm_lldp_listener_is_running (NMLldpListener *self);
 
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index c3d48b41..60842c18 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -107,7 +107,8 @@ complete_connection (NMDevice *device,
 {
 	NMSettingTeam *s_team;
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_TEAM_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/tests/test-arping.c b/src/devices/tests/test-arping.c
index 36e17631..72d28a7a 100644
--- a/src/devices/tests/test-arping.c
+++ b/src/devices/tests/test-arping.c
@@ -41,8 +41,8 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 {
 	/* create veth pair. */
 	nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1);
-	fixture->ifindex0 = nmtstp_assert_wait_for_link (IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
-	fixture->ifindex1 = nmtstp_assert_wait_for_link (IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
+	fixture->ifindex0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
+	fixture->ifindex1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
 
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL));
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL));
@@ -80,7 +80,7 @@ test_arping_common (test_fixture *fixture, TestInfo *info)
 
 	for (i = 0; info->peer_addresses[i]; i++) {
 		nmtstp_ip4_address_add (FALSE, fixture->ifindex1, info->peer_addresses[i],
-		                        24, 0, 3600, 1800, NULL);
+		                        24, 0, 3600, 1800, 0, NULL);
 	}
 
 	loop = g_main_loop_new (NULL, FALSE);
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index dfae58b1..85c3b510 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -27,18 +27,327 @@
 #include <sys/types.h>
 
 #include "nm-lldp-listener.h"
+#include "nm-sd.h"
+
+#include "nm-sd-adapt.h"
+#include "lldp.h"
+
 #include "test-common.h"
 
+#include "nm-test-utils.h"
+
+/*****************************************************************************/
+
+static GVariant *
+get_lldp_neighbor (GVariant *neighbors,
+                   int chassis_id_type,
+                   const char *chassis_id,
+                   int port_id_type,
+                   const char *port_id)
+{
+	GVariantIter iter;
+	GVariant *variant;
+	GVariant *result = NULL;
+
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+
+	g_assert (chassis_id_type >= -1 && chassis_id_type <= G_MAXUINT8);
+	g_assert (port_id_type >= -1 && port_id_type <= G_MAXUINT8);
+
+	g_variant_iter_init (&iter, neighbors);
+	while (g_variant_iter_next (&iter, "@a{sv}", &variant)) {
+		gs_unref_variant GVariant *v_chassis_id_type = NULL;
+		gs_unref_variant GVariant *v_chassis_id = NULL;
+		gs_unref_variant GVariant *v_port_id_type = NULL;
+		gs_unref_variant GVariant *v_port_id = NULL;
+
+		v_chassis_id_type = g_variant_lookup_value (variant, NM_LLDP_ATTR_CHASSIS_ID_TYPE, G_VARIANT_TYPE_UINT32);
+		g_assert (v_chassis_id_type);
+
+		v_chassis_id = g_variant_lookup_value (variant, NM_LLDP_ATTR_CHASSIS_ID, G_VARIANT_TYPE_STRING);
+		g_assert (v_chassis_id);
+
+		v_port_id_type = g_variant_lookup_value (variant, NM_LLDP_ATTR_PORT_ID_TYPE, G_VARIANT_TYPE_UINT32);
+		g_assert (v_port_id_type);
+
+		v_port_id = g_variant_lookup_value (variant, NM_LLDP_ATTR_PORT_ID, G_VARIANT_TYPE_STRING);
+		g_assert (v_port_id);
+
+		if (   nm_streq (g_variant_get_string (v_chassis_id, NULL), chassis_id)
+		    && nm_streq (g_variant_get_string (v_port_id, NULL), port_id)
+		    && NM_IN_SET (chassis_id_type, -1, g_variant_get_uint32 (v_chassis_id_type))
+		    && NM_IN_SET (port_id_type, -1, g_variant_get_uint32 (v_port_id_type))) {
+			g_assert (!result);
+			result = variant;
+		} else
+			g_variant_unref (variant);
+	}
+
+	return result;
+}
+
 typedef struct {
 	int ifindex;
 	int fd;
 	guint8 mac[ETH_ALEN];
-} test_fixture;
+} TestRecvFixture;
+
+typedef struct {
+	gsize frame_len;
+	const uint8_t *frame;
+} TestRecvFrame;
+#define TEST_RECV_FRAME_DEFINE(name, ...) \
+	static const guint8 _##name##_v[] = { __VA_ARGS__ }; \
+	static const TestRecvFrame name = { \
+		.frame_len = sizeof (_##name##_v), \
+		.frame = _##name##_v, \
+	}
+
+typedef struct {
+	guint expected_num_called;
+	gsize frames_len;
+	const TestRecvFrame *frames[10];
+	void (*check) (GMainLoop *loop, NMLldpListener *listener);
+} TestRecvData;
+#define TEST_RECV_DATA_DEFINE(name, _expected_num_called, _check, ...) \
+	static const TestRecvData name = { \
+		.expected_num_called = _expected_num_called, \
+		.check = _check, \
+		.frames_len = NM_NARG (__VA_ARGS__), \
+		.frames = { __VA_ARGS__ }, \
+	}
 
 #define TEST_IFNAME "nm-tap-test0"
 
+TEST_RECV_FRAME_DEFINE (_test_recv_data0_frame0,
+	/* Ethernet header */
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+	0x88, 0xcc,                             /* Ethertype */
+	/* LLDP mandatory TLVs */
+	0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+	0x03, 0x04, 0x05,
+	0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+	0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
+	/* LLDP optional TLVs */
+	0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
+	0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
+	0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
+	0x00, 0x00                              /* End Of LLDPDU */
+);
+
+static void
+_test_recv_data0_check (GMainLoop *loop, NMLldpListener *listener)
+{
+	GVariant *neighbors, *attr;
+	gs_unref_variant GVariant *neighbor = NULL;
+
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpint (g_variant_n_children (neighbors), ==, 1);
+
+	neighbor = get_lldp_neighbor (neighbors,
+	                              LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:02:03:04:05",
+	                              LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/3");
+	g_assert (neighbor);
+	g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 4);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_PORT_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Port");
+	nm_clear_g_variant (&attr);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_NAME, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "SYS");
+	nm_clear_g_variant (&attr);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE);
+	nm_clear_g_variant (&attr);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "foo");
+	nm_clear_g_variant (&attr);
+}
+
+TEST_RECV_DATA_DEFINE (_test_recv_data0,       1, _test_recv_data0_check,  &_test_recv_data0_frame0);
+TEST_RECV_DATA_DEFINE (_test_recv_data0_twice, 1, _test_recv_data0_check,  &_test_recv_data0_frame0, &_test_recv_data0_frame0);
+
+
+TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0,
+	/* lldp.detailed.pcap from
+	 * https://wiki.wireshark.org/SampleCaptures#Link_Layer_Discovery_Protocol_.28LLDP.29 */
+
+	/* ethernet header */
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, /* destination mac */
+	0x00, 0x01, 0x30, 0xf9, 0xad, 0xa0, /* source mac */
+	0x88, 0xcc,                         /* ethernet type */
+
+	0x02, 0x07, 0x04, 0x00, 0x01, 0x30, /* Chassis Subtype */
+	0xf9, 0xad, 0xa0,
+	0x04, 0x04, 0x05, 0x31, 0x2f, 0x31, /* Port Subtype */
+	0x06, 0x02, 0x00, 0x78,             /* Time To Live */
+	0x08, 0x17, 0x53, 0x75, 0x6d, 0x6d, /* Port Description */
+	0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
+	0x34, 0x38, 0x2d, 0x50, 0x6f, 0x72,
+	0x74, 0x20, 0x31, 0x30, 0x30, 0x31,
+	0x00,
+	0x0a, 0x0d, 0x53, 0x75, 0x6d, 0x6d, /* System Name */
+	0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
+	0x34, 0x38, 0x00,
+	0x0c, 0x4c, 0x53, 0x75, 0x6d, 0x6d, /* System Description */
+	0x69, 0x74, 0x33, 0x30, 0x30, 0x2d,
+	0x34, 0x38, 0x20, 0x2d, 0x20, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x20, 0x37, 0x2e, 0x34, 0x65, 0x2e,
+	0x31, 0x20, 0x28, 0x42, 0x75, 0x69,
+	0x6c, 0x64, 0x20, 0x35, 0x29, 0x20,
+	0x62, 0x79, 0x20, 0x52, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x4d,
+	0x61, 0x73, 0x74, 0x65, 0x72, 0x20,
+	0x30, 0x35, 0x2f, 0x32, 0x37, 0x2f,
+	0x30, 0x35, 0x20, 0x30, 0x34, 0x3a,
+	0x35, 0x33, 0x3a, 0x31, 0x31, 0x00,
+	0x0e, 0x04, 0x00, 0x14, 0x00, 0x14, /* Capabilities */
+	0x10, 0x0e, 0x07, 0x06, 0x00, 0x01, /* Management Address */
+	0x30, 0xf9, 0xad, 0xa0, 0x02, 0x00,
+	0x00, 0x03, 0xe9, 0x00,
+	0xfe, 0x07, 0x00, 0x12, 0x0f, 0x02, /* IEEE 802.3 - Power Via MDI */
+	0x07, 0x01, 0x00,
+	0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, /* IEEE 802.3 - MAC/PHY Configuration/Status */
+	0x03, 0x6c, 0x00, 0x00, 0x10,
+	0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, /* IEEE 802.3 - Link Aggregation */
+	0x01, 0x00, 0x00, 0x00, 0x00,
+	0xfe, 0x06, 0x00, 0x12, 0x0f, 0x04, /* IEEE 802.3 - Maximum Frame Size */
+	0x05, 0xf2,
+	0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* IEEE 802.1 - Port VLAN ID */
+	0x01, 0xe8,
+	0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02, /* IEEE 802.1 - Port and Protocol VLAN ID */
+	0x01, 0x00, 0x00,
+	0xfe, 0x17, 0x00, 0x80, 0xc2, 0x03, /* IEEE 802.1 - VLAN Name */
+	0x01, 0xe8, 0x10, 0x76, 0x32, 0x2d,
+	0x30, 0x34, 0x38, 0x38, 0x2d, 0x30,
+	0x33, 0x2d, 0x30, 0x35, 0x30, 0x35,
+	0x00,
+	0xfe, 0x05, 0x00, 0x80, 0xc2, 0x04, /* IEEE 802.1 - Protocol Identity */
+	0x00,
+	0x00, 0x00                          /* End of LLDPDU */
+);
+
 static void
-fixture_setup (test_fixture *fixture, gconstpointer user_data)
+_test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
+{
+	GVariant *neighbors, *attr;
+	gs_unref_variant GVariant *neighbor = NULL;
+
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpint (g_variant_n_children (neighbors), ==, 1);
+
+	neighbor = get_lldp_neighbor (neighbors,
+	                              LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0",
+	                              LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1");
+	g_assert (neighbor);
+	g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 10);
+
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_BRIDGE);
+	nm_clear_g_variant (&attr);
+
+	/* unsupported: Time To Live */
+
+	/* Port Description */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_PORT_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Summit300-48-Port 1001");
+	nm_clear_g_variant (&attr);
+
+	/* System Name */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_NAME, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Summit300-48");
+	nm_clear_g_variant (&attr);
+
+	/* System Description */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_DESCRIPTION, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "Summit300-48 - Version 7.4e.1 (Build 5) by Release_Master 05/27/05 04:53:11");
+	nm_clear_g_variant (&attr);
+
+	/* Capabilities */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_SYSTEM_CAPABILITIES, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 20);
+	nm_clear_g_variant (&attr);
+
+	/* unsupported: Management Address */
+	/* unsupported: IEEE 802.3 - Power Via MDI */
+	/* unsupported: IEEE 802.3 - MAC/PHY Configuration/Status */
+	/* unsupported: IEEE 802.3 - Link Aggregation */
+	/* unsupported: IEEE 802.3 - Maximum Frame Size*/
+
+	/* IEEE 802.1 - Port VLAN ID */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PVID, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 488);
+	nm_clear_g_variant (&attr);
+
+	/* IEEE 802.1 - Port and Protocol VLAN ID */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVID, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 0);
+	nm_clear_g_variant (&attr);
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 1);
+	nm_clear_g_variant (&attr);
+
+	/* IEEE 802.1 - VLAN Name */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, G_VARIANT_TYPE_STRING);
+	nmtst_assert_variant_string (attr, "v2-0488-03-0505");
+	nm_clear_g_variant (&attr);
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VID, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 488);
+	nm_clear_g_variant (&attr);
+
+	/* unsupported: IEEE 802.1 - Protocol Identity */
+}
+
+TEST_RECV_DATA_DEFINE (_test_recv_data1,       1, _test_recv_data1_check,  &_test_recv_data1_frame0);
+
+TEST_RECV_FRAME_DEFINE (_test_recv_data2_frame0_ttl1,
+	/* Ethernet header */
+	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+	0x88, 0xcc,                             /* Ethertype */
+	/* LLDP mandatory TLVs */
+	0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+	0x03, 0x04, 0x05,
+	0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+	0x06, 0x02, 0x00, 0x01,                 /* TTL: 1 seconds */
+	/* LLDP optional TLVs */
+	0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
+	0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
+	0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
+	0x00, 0x00                              /* End Of LLDPDU */
+);
+
+static void
+_test_recv_data2_ttl1_check (GMainLoop *loop, NMLldpListener *listener)
+{
+	gulong notify_id;
+	GVariant *neighbors;
+
+	_test_recv_data0_check (loop, listener);
+
+	/* wait for signal. */
+	notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+	                              nmtst_main_loop_quit_on_notify, loop);
+	if (!nmtst_main_loop_run (loop, 5000))
+		g_assert_not_reached ();
+	nm_clear_g_signal_handler (listener, &notify_id);
+
+	neighbors = nm_lldp_listener_get_neighbors (listener);
+	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpint (g_variant_n_children (neighbors), ==, 0);
+}
+
+TEST_RECV_DATA_DEFINE (_test_recv_data2_ttl1, 1, _test_recv_data2_ttl1_check,  &_test_recv_data2_frame0_ttl1);
+
+static void
+_test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 {
 	const NMPlatformLink *link;
 	struct ifreq ifr = { };
@@ -48,7 +357,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 	g_assert (fd >= 0);
 
 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-	strncpy (ifr.ifr_name, TEST_IFNAME, IFNAMSIZ);
+	nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
 	g_assert (ioctl (fd, TUNSETIFF, &ifr) >= 0);
 
 	/* Bring the interface up */
@@ -58,9 +367,7 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 	g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
 	close (s);
 
-	nm_platform_process_events (NM_PLATFORM_GET);
-	link = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, TEST_IFNAME);
-	g_assert (link);
+	link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TAP, 100);
 	fixture->ifindex = link->ifindex;
 	fixture->fd = fd;
 	memcpy (fixture->mac, link->addr.data, ETH_ALEN);
@@ -68,149 +375,66 @@ fixture_setup (test_fixture *fixture, gconstpointer user_data)
 
 typedef struct {
 	int num_called;
-} TestInfo;
-
-static gboolean
-loop_quit (gpointer user_data)
-{
-	g_main_loop_quit ((GMainLoop *) user_data);
-	return G_SOURCE_REMOVE;
-}
+} TestRecvCallbackInfo;
 
 static void
 lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec,
                         gpointer user_data)
 {
-	TestInfo *info = user_data;
+	TestRecvCallbackInfo *info = user_data;
 
 	info->num_called++;
 }
 
-static GVariant *
-get_lldp_neighbor_attribute (GVariant *neighbors,
-                             const char *chassis, const char *port,
-                             const char *name)
-{
-	GVariantIter iter, attrs_iter;
-	GVariant *variant, *attr_variant;
-	const char *attr_name;
-
-	g_return_val_if_fail (g_variant_is_of_type (neighbors,
-	                                            G_VARIANT_TYPE ("aa{sv}")),
-	                      NULL);
-	g_variant_iter_init (&iter, neighbors);
-
-	while (g_variant_iter_next (&iter, "@a{sv}", &variant)) {
-		gs_unref_variant GVariant *chassis_v = NULL;
-		gs_unref_variant GVariant *port_v = NULL;
-		gs_unref_variant GVariant *attr_v = NULL;
-
-		g_variant_iter_init (&attrs_iter, variant);
-		while (g_variant_iter_next (&attrs_iter, "{&sv}", &attr_name, &attr_variant)) {
-			if (!g_strcmp0 (attr_name, NM_LLDP_ATTR_CHASSIS_ID)) {
-				g_assert (!chassis_v);
-				chassis_v = attr_variant;
-			} else if (!g_strcmp0 (attr_name, NM_LLDP_ATTR_PORT_ID)) {
-				g_assert (!port_v);
-				port_v = attr_variant;
-			} else if (!g_strcmp0 (attr_name, name)) {
-				g_assert (!attr_v);
-				attr_v = attr_variant;
-			} else
-				g_variant_unref (attr_variant);
-		}
-
-		g_variant_unref (variant);
-
-		if (   chassis_v
-		    && port_v
-		    && g_variant_is_of_type (chassis_v, G_VARIANT_TYPE_STRING)
-		    && g_variant_is_of_type (port_v, G_VARIANT_TYPE_STRING)
-		    && !g_strcmp0 (chassis, g_variant_get_string (chassis_v, NULL))
-		    && !g_strcmp0 (port, g_variant_get_string (port_v, NULL)))
-			return g_variant_ref (attr_v);
-	}
-
-	/* neighbor not found */
-	return NULL;
-}
-
 static void
-test_receive_frame (test_fixture *fixture, gconstpointer user_data)
+test_recv (TestRecvFixture *fixture, gconstpointer user_data)
 {
+	const TestRecvData *data = user_data;
 	gs_unref_object NMLldpListener *listener = NULL;
 	GMainLoop *loop;
-	TestInfo info = { };
-	GVariant *neighbors, *attr;
-	uint8_t frame[] = {
-		/* Ethernet header */
-		0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC */
-		0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
-		0x88, 0xcc,                             /* Ethertype */
-		/* LLDP mandatory TLVs */
-		0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
-		0x03, 0x04, 0x05,
-		0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
-		0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds */
-		/* LLDP optional TLVs */
-		0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
-		0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
-		0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
-		0x00, 0x00                              /* End Of LLDPDU */
-	};
+	TestRecvCallbackInfo info = { };
+	gsize i_frames;
+	gulong notify_id;
+	GError *error = NULL;
+	guint sd_id;
 
 	listener = nm_lldp_listener_new ();
 	g_assert (listener != NULL);
-	g_assert (nm_lldp_listener_start (listener, fixture->ifindex, TEST_IFNAME, fixture->mac, ETH_ALEN, NULL));
+	g_assert (nm_lldp_listener_start (listener, fixture->ifindex, &error));
+	g_assert_no_error (error);
 
-	g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
-	                  (GCallback) lldp_neighbors_changed, &info);
+	notify_id = g_signal_connect (listener, "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+	                              (GCallback) lldp_neighbors_changed, &info);
 	loop = g_main_loop_new (NULL, FALSE);
-	g_timeout_add_seconds (1, loop_quit, loop);
+	sd_id = nm_sd_event_attach_default ();
+
+	for (i_frames = 0; i_frames < data->frames_len; i_frames++) {
+		const TestRecvFrame *f = data->frames[i_frames];
 
-	g_assert (write (fixture->fd, frame, sizeof (frame)) == sizeof (frame));
-	g_assert (write (fixture->fd, frame, sizeof (frame)) == sizeof (frame));
+		g_assert (write (fixture->fd, f->frame, f->frame_len) == f->frame_len);
+	}
 
-	g_main_loop_run (loop);
+	if (nmtst_main_loop_run (loop, 500))
+		g_assert_not_reached ();
 
-	g_assert_cmpint (info.num_called, ==, 1);
-	neighbors = nm_lldp_listener_get_neighbors (listener);
-	g_assert (neighbors != NULL);
-
-	/* Check port description */
-	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
-	                                    NM_LLDP_ATTR_PORT_DESCRIPTION);
-	g_assert (attr != NULL);
-	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
-	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==, "Port");
-	nm_clear_g_variant (&attr);
+	g_assert_cmpint (info.num_called, ==, data->expected_num_called);
 
-	/* Check system name */
-	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
-	                                    NM_LLDP_ATTR_SYSTEM_NAME);
-	g_assert (attr != NULL);
-	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
-	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==, "SYS");
-	nm_clear_g_variant (&attr);
+	nm_clear_g_signal_handler (listener, &notify_id);
 
-	/* Check destination */
-	attr = get_lldp_neighbor_attribute (neighbors, "00:01:02:03:04:05", "1/3",
-	                                    NM_LLDP_ATTR_DESTINATION);
-	g_assert (attr != NULL);
-	g_assert (g_variant_is_of_type (attr, G_VARIANT_TYPE_STRING));
-	g_assert_cmpstr (g_variant_get_string (attr, NULL), ==,
-	                 NM_LLDP_DEST_NEAREST_NON_TPMR_BRIDGE);
-	nm_clear_g_variant (&attr);
+	data->check (loop, listener);
 
+	nm_clear_g_source (&sd_id);
 	g_clear_pointer (&loop, g_main_loop_unref);
 }
 
 static void
-fixture_teardown (test_fixture *fixture, gconstpointer user_data)
+_test_recv_fixture_teardown (TestRecvFixture *fixture, gconstpointer user_data)
 {
 	nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex);
 }
 
+/*****************************************************************************/
+
 void
 init_tests (int *argc, char ***argv)
 {
@@ -220,6 +444,10 @@ init_tests (int *argc, char ***argv)
 void
 setup_tests (void)
 {
-	g_test_add ("/lldp/receive_frame", test_fixture, NULL, fixture_setup,
-	            test_receive_frame, fixture_teardown);
+#define _TEST_ADD_RECV(testpath, testdata) \
+	g_test_add (testpath, TestRecvFixture, testdata, _test_recv_fixture_setup, test_recv, _test_recv_fixture_teardown)
+	_TEST_ADD_RECV ("/lldp/recv/0",       &_test_recv_data0);
+	_TEST_ADD_RECV ("/lldp/recv/0_twice", &_test_recv_data0_twice);
+	_TEST_ADD_RECV ("/lldp/recv/1",       &_test_recv_data1);
+	_TEST_ADD_RECV ("/lldp/recv/2_ttl1",  &_test_recv_data2_ttl1);
 }
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 11da011e..6fbc63a1 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -138,7 +138,8 @@ complete_connection (NMDevice *device,
 
 	}
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_OLPC_MESH_SETTING_NAME,
 	                           existing_connections,
 	                           NULL,
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index da2a0f4a..95a89eab 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -845,7 +845,8 @@ complete_connection (NMDevice *device,
 
 	str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
 
-	nm_utils_complete_generic (connection,
+	nm_utils_complete_generic (NM_PLATFORM_GET,
+	                           connection,
 	                           NM_SETTING_WIRELESS_SETTING_NAME,
 	                           existing_connections,
 	                           str_ssid,
@@ -2491,7 +2492,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	if (config == NULL) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: (wifi) couldn't build wireless configuration: %s",
-		       error ? error->message : "<BUG>");
+		       error->message);
 		g_clear_error (&error);
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		goto out;
@@ -2506,7 +2507,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	if (!nm_supplicant_interface_set_config (priv->sup_iface, config, &error)) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: (wifi) couldn't send wireless configuration to the supplicant: %s",
-		       error ? error->message : "<BUG>");
+		       error->message);
 		g_clear_error (&error);
 		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
 		goto out;
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index 307825c6..a0c25709 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -741,7 +741,7 @@ nm_ap_dump (NMAccessPoint *self,
 
 	nm_log_dbg (LOGD_WIFI_SCAN, "%s[%s%c] %-32s[%s%u %3u%% %c W:%04X R:%04X] [%3u] %s%s",
 	            prefix,
-	            str_if_set (priv->address, "(none)"),
+	            priv->address ?: "(none)",
 	            mode_to_char (self),
 	            priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)",
 	            chan > 99 ? "" : (chan > 9 ? " " : "  "),
diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c
index 01990bee..838368c4 100644
--- a/src/devices/wifi/tests/test-wifi-ap-utils.c
+++ b/src/devices/wifi/tests/test-wifi-ap-utils.c
@@ -37,7 +37,7 @@
 	if (expected) { \
 		if (!success) { \
 			g_assert (error != NULL); \
-			g_warning ("Failed to complete connection: (%d) %s", error->code, error->message); \
+			g_warning ("Failed to complete connection: %s", error->message); \
 		} \
 		g_assert (success == TRUE); \
 		g_assert (error == NULL); \
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index e9c4f02e..a8361c49 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -157,8 +157,8 @@ modem_ip4_config_result (NMModem *modem,
 	g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE);
 
 	if (error) {
-		_LOGW (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: (%d) %s",
-		       error->code, error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: %s",
+		       error->message);
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	} else {
@@ -184,8 +184,7 @@ modem_ip6_config_result (NMModem *modem,
 	g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE);
 
 	if (error) {
-		_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: (%d) %s",
-		       error->code, error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: %s", error->message);
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return;
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 6343416c..769e6a27 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -464,7 +464,7 @@ connect_context_step (NMModemBroadband *self)
 			nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
 			             nm_modem_get_uid (NM_MODEM (self)),
 			             nm_connection_get_id (ctx->connection),
-			             error ? error->message : "unknown error");
+			             error->message);
 			g_clear_error (&error);
 
 			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
@@ -647,7 +647,8 @@ complete_connection (NMModem *_self,
 		if (!nm_setting_gsm_get_number (s_gsm))
 			g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL);
 
-		nm_utils_complete_generic (connection,
+		nm_utils_complete_generic (NM_PLATFORM_GET,
+		                           connection,
 		                           NM_SETTING_GSM_SETTING_NAME,
 		                           existing_connections,
 		                           NULL,
@@ -670,7 +671,8 @@ complete_connection (NMModem *_self,
 		if (!nm_setting_cdma_get_number (s_cdma))
 			g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL);
 
-		nm_utils_complete_generic (connection,
+		nm_utils_complete_generic (NM_PLATFORM_GET,
+		                           connection,
 		                           NM_SETTING_CDMA_SETTING_NAME,
 		                           existing_connections,
 		                           NULL,
@@ -734,7 +736,7 @@ set_power_state_low_ready (MMModem *modem,
 		/* Log but ignore errors; not all modems support low power state */
 		nm_log_dbg (LOGD_MB, "(%s): failed to set modem low power state: %s",
 		            nm_modem_get_uid (NM_MODEM (self)),
-		            error && error->message ? error->message : "(unknown)");
+		            NM_G_ERROR_MSG (error));
 		g_clear_error (&error);
 	}
 
@@ -759,7 +761,7 @@ modem_disable_ready (MMModem *modem_iface,
 	} else {
 		nm_log_warn (LOGD_MB, "(%s): failed to disable modem: %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
-		             error && error->message ? error->message : "(unknown)");
+		             NM_G_ERROR_MSG (error));
 		nm_modem_set_prev_state (NM_MODEM (self), "disable failed");
 		g_clear_error (&error);
 	}
@@ -778,7 +780,7 @@ modem_enable_ready (MMModem *modem_iface,
 	if (!mm_modem_enable_finish (modem_iface, res, &error)) {
 		nm_log_warn (LOGD_MB, "(%s) failed to enable modem: %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
-		             error && error->message ? error->message : "(unknown)");
+		             NM_G_ERROR_MSG (error));
 		nm_modem_set_prev_state (NM_MODEM (self), "enable failed");
 		g_clear_error (&error);
 	}
@@ -1259,7 +1261,7 @@ get_sim_ready (MMModem *modem,
 	} else {
 		nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s",
 		             nm_modem_get_uid (NM_MODEM (self)),
-		             error && error->message ? error->message : "(unknown)");
+		             NM_G_ERROR_MSG (error));
 	}
 	g_clear_error (&error);
 	g_object_unref (self);
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index 79082c41..e26321b0 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -145,7 +145,7 @@ modem_object_added (MMManager *modem_manager,
 		handle_new_modem (self, modem);
 	else {
 		nm_log_warn (LOGD_MB, "failed to create modem: %s",
-		             error ? error->message : "(unknown)");
+		             error->message);
 	}
 	g_clear_error (&error);
 }
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index c1736ecf..2a8a9d1e 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -545,10 +545,9 @@ ppp_stage3_ip_config_start (NMModem *self,
 
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else {
-		nm_log_err (LOGD_PPP, "(%s): error starting PPP: (%d) %s",
+		nm_log_err (LOGD_PPP, "(%s): error starting PPP: %s",
 		            nm_modem_get_uid (self),
-		            error ? error->code : -1,
-		            error && error->message ? error->message : "(unknown)");
+		            error->message);
 		g_error_free (error);
 
 		nm_exported_object_clear_and_unexport (&priv->ppp_manager);
diff --git a/src/dhcp-manager/nm-dhcp-client-logging.h b/src/dhcp-manager/nm-dhcp-client-logging.h
new file mode 100644
index 00000000..bb4f2f4a
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-client-logging.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DHCP_CLIENT_LOGGING_H__
+#define __NETWORKMANAGER_DHCP_CLIENT_LOGGING_H__
+
+#include "nm-default.h"
+#include "nm-dhcp-client.h"
+
+#define _NMLOG_PREFIX_NAME    "dhcp"
+#define _NMLOG_DOMAIN         LOGD_DHCP
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel _level = (level); \
+        \
+        /* we check first for LOGD_DHCP instead of the correct domain.
+         * In the worst case, we guess wrong and enter the block.
+         *
+         * Same for the _NMLOG_ENABLED() macro. Probably it would be more
+         * expensive to determine the correct value then what we could
+         * safe. */ \
+        if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \
+            NMDhcpClient *_self = (NMDhcpClient *) (self); \
+            const char *__ifname = _self ? nm_dhcp_client_get_iface (_self) : NULL; \
+            const NMLogDomain _domain = !_self \
+                                            ? LOGD_DHCP \
+                                            : (nm_dhcp_client_get_ipv6 (_self) ? LOGD_DHCP6 : LOGD_DHCP4); \
+            \
+            nm_log (_level, _domain, \
+                    "%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                    _NMLOG_PREFIX_NAME, \
+                    (_domain == LOGD_DHCP4 ? "4" : (_domain == LOGD_DHCP6 ? "6" : "")), \
+                    NM_PRINT_FMT_QUOTED (__ifname, " (", __ifname, ")", "") \
+                    _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+#endif /* __NETWORKMANAGER_DHCP_CLIENT_LOGGING_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index 0aeb6118..b8161ab5 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -35,6 +35,8 @@
 #include "nm-dhcp-utils.h"
 #include "nm-platform.h"
 
+#include "nm-dhcp-client-logging.h"
+
 typedef struct {
 	char *       iface;
 	int          ifindex;
@@ -204,7 +206,7 @@ state_to_string (NMDhcpState state)
 }
 
 static NMDhcpState
-reason_to_state (const char *iface, const char *reason)
+reason_to_state (NMDhcpClient *self, const char *iface, const char *reason)
 {
 	if (g_ascii_strcasecmp (reason, "bound") == 0 ||
 	    g_ascii_strcasecmp (reason, "bound6") == 0 ||
@@ -226,7 +228,7 @@ reason_to_state (const char *iface, const char *reason)
 	         g_ascii_strcasecmp (reason, "abend") == 0)
 		return NM_DHCP_STATE_FAIL;
 
-	nm_log_dbg (LOGD_DHCP, "(%s): unmapped DHCP state '%s'", iface, reason);
+	_LOGD ("unmapped DHCP state '%s'", reason);
 	return NM_DHCP_STATE_UNKNOWN;
 }
 
@@ -319,13 +321,10 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 			event_id = g_strdup_printf ("%s|%s", iaid, start);
 	}
 
-	nm_log_info (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
-	             "(%s): DHCPv%c state changed %s -> %s%s%s%s",
-	             priv->iface,
-	             priv->ipv6 ? '6' : '4',
-	             state_to_string (priv->state),
-	             state_to_string (new_state),
-	             NM_PRINT_FMT_QUOTED (event_id, ", event ID=\"", event_id, "\"", ""));
+	_LOGI ("state changed %s -> %s%s%s%s",
+	       state_to_string (priv->state),
+	       state_to_string (new_state),
+	       NM_PRINT_FMT_QUOTED (event_id, ", event ID=\"", event_id, "\"", ""));
 
 	priv->state = new_state;
 	g_signal_emit (G_OBJECT (self),
@@ -343,10 +342,7 @@ transaction_timeout (gpointer user_data)
 	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 
 	priv->timeout_id = 0;
-	nm_log_warn (priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4,
-	             "(%s): DHCPv%c request timed out.",
-	             priv->iface,
-	             priv->ipv6 ? '6' : '4');
+	_LOGW ("request timed out");
 	nm_dhcp_client_set_state (self, NM_DHCP_STATE_TIMEOUT, NULL, NULL);
 	return G_SOURCE_REMOVE;
 }
@@ -357,29 +353,20 @@ daemon_watch_cb (GPid pid, gint status, gpointer user_data)
 	NMDhcpClient *self = NM_DHCP_CLIENT (user_data);
 	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 	NMDhcpState new_state;
-	guint64 log_domain;
-	guint ip_ver;
 
 	g_return_if_fail (priv->watch_id);
 	priv->watch_id = 0;
 
-	log_domain = priv->ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
-	ip_ver = priv->ipv6 ? 6 : 4;
-
 	if (WIFEXITED (status))
-		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d exited with status %d",
-		             priv->iface, ip_ver, pid, WEXITSTATUS (status));
+		_LOGI ("client pid %d exited with status %d", pid, WEXITSTATUS (status));
 	else if (WIFSIGNALED (status))
-		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d killed by signal %d",
-		             priv->iface, ip_ver, pid, WTERMSIG (status));
+		_LOGI ("client pid %d killed by signal %d", pid, WTERMSIG (status));
 	else if (WIFSTOPPED(status))
-		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d stopped by signal %d",
-		             priv->iface, ip_ver, pid, WSTOPSIG (status));
+		_LOGI ("client pid %d stopped by signal %d", pid, WSTOPSIG (status));
 	else if (WIFCONTINUED (status))
-		nm_log_info (log_domain, "(%s): DHCPv%d client pid %d resumed (by SIGCONT)",
-		             priv->iface, ip_ver, pid);
+		_LOGI ("client pid %d resumed (by SIGCONT)", pid);
 	else
-		nm_log_warn (LOGD_DHCP, "DHCP client died abnormally");
+		_LOGW ("client died abnormally");
 
 	if (!WIFEXITED (status))
 		new_state = NM_DHCP_STATE_FAIL;
@@ -426,6 +413,7 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
                           const char *last_ip4_address)
 {
 	NMDhcpClientPrivate *priv;
+	gs_unref_bytes GBytes *tmp = NULL;
 
 	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
 
@@ -434,10 +422,11 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
 	g_return_val_if_fail (priv->ipv6 == FALSE, FALSE);
 	g_return_val_if_fail (priv->uuid != NULL, FALSE);
 
-	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
-	             priv->iface, priv->timeout);
+	_LOGI ("activation: beginning transaction (timeout in %d seconds)", priv->timeout);
 
-	nm_dhcp_client_set_client_id (self, dhcp_client_id ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) : NULL);
+	if (dhcp_client_id)
+		tmp = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id);
+	nm_dhcp_client_set_client_id (self, tmp);
 
 	g_clear_pointer (&priv->hostname, g_free);
 	priv->hostname = g_strdup (hostname);
@@ -506,7 +495,7 @@ generate_duid_from_machine_id (void)
 	}
 
 	if (!success) {
-		nm_log_warn (LOGD_DHCP6, "Failed to read " SYSCONFDIR "/machine-id "
+		nm_log_warn (LOGD_DHCP6, "dhcp6: failed to read " SYSCONFDIR "/machine-id "
 		             "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate "
 		             "DHCPv6 DUID; creating non-persistent random DUID.");
 
@@ -546,7 +535,7 @@ get_duid (NMDhcpClient *self)
 
 		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
 			str = nm_dhcp_utils_duid_to_string (duid);
-			nm_log_dbg (LOGD_DHCP6, "Generated DUID %s", str);
+			_LOGD ("generated DUID %s", str);
 			g_free (str);
 		}
 	}
@@ -583,9 +572,9 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 	if (!priv->duid)
 		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP)) {
+	if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
 		str = nm_dhcp_utils_duid_to_string (priv->duid);
-		nm_log_dbg (LOGD_DHCP, "(%s): DHCPv6 DUID is '%s'", priv->iface, str);
+		_LOGD ("DUID is '%s'", str);
 		g_free (str);
 	}
 
@@ -594,8 +583,8 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 
 	priv->info_only = info_only;
 
-	nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
-	             priv->iface, priv->timeout);
+	_LOGI ("activation: beginning transaction (timeout in %d seconds)",
+	       priv->timeout);
 
 	return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
 	                                                   dhcp_anycast_addr,
@@ -645,8 +634,10 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
 		}
 	}
 
-	if (remove (pid_file) == -1)
-		nm_log_dbg (LOGD_DHCP, "Could not remove dhcp pid file \"%s\": %d (%s)", pid_file, errno, g_strerror (errno));
+	if (remove (pid_file) == -1) {
+		nm_log_dbg (LOGD_DHCP, "dhcp: could not remove pid file \"%s\": %d (%s)",
+		            pid_file, errno, g_strerror (errno));
+	}
 
 	g_free (proc_path);
 	g_free (pid_contents);
@@ -666,11 +657,10 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release)
 	/* Kill the DHCP client */
 	old_pid = priv->pid;
 	NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid);
-	if (old_pid > 0) {
-		nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction, DHCP client pid %d",
-		             priv->iface, old_pid);
-	} else
-		nm_log_info (LOGD_DHCP, "(%s): canceled DHCP transaction", priv->iface);
+	if (old_pid > 0)
+		_LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid);
+	else
+		_LOGI ("canceled DHCP transaction");
 	g_assert (priv->pid == -1);
 
 	nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL);
@@ -710,7 +700,7 @@ bytearray_variant_to_string (GVariant *value, const char *key)
 
 	converted = str->str;
 	if (!g_utf8_validate (converted, -1, NULL))
-		nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
+		nm_log_warn (LOGD_DHCP, "dhcp: option '%s' couldn't be converted to UTF-8", key);
 	g_string_free (str, FALSE);
 	return converted;
 }
@@ -782,9 +772,9 @@ nm_dhcp_client_handle_event (gpointer unused,
 		return FALSE;
 
 	old_state = priv->state;
-	new_state = reason_to_state (priv->iface, reason);
-	nm_log_dbg (LOGD_DHCP, "(%s): DHCP reason '%s' -> state '%s'",
-	            iface, reason, state_to_string (new_state));
+	new_state = reason_to_state (self, priv->iface, reason);
+	_LOGD ("DHCP reason '%s' -> state '%s'",
+	       reason, state_to_string (new_state));
 
 	if (new_state == NM_DHCP_STATE_BOUND) {
 		GVariantIter iter;
@@ -817,7 +807,7 @@ nm_dhcp_client_handle_event (gpointer unused,
 
 			/* Fail if no valid IP config was received */
 			if (ip_config == NULL) {
-				nm_log_warn (LOGD_DHCP, "(%s): DHCP client bound but IP config not received", iface);
+				_LOGW ("client bound but IP config not received");
 				new_state = NM_DHCP_STATE_FAIL;
 				g_clear_pointer (&str_options, g_hash_table_unref);
 			}
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index bc4345fe..4b6d301b 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -41,6 +41,7 @@
 #include "nm-dhcp-manager.h"
 #include "NetworkManagerUtils.h"
 #include "nm-dhcp-listener.h"
+#include "nm-dhcp-client-logging.h"
 
 G_DEFINE_TYPE (NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
 
@@ -148,7 +149,8 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
 }
 
 static gboolean
-merge_dhclient_config (const char *iface,
+merge_dhclient_config (NMDhcpDhclient *self,
+                       const char *iface,
                        const char *conf_file,
                        gboolean is_ip6,
                        GBytes *client_id,
@@ -169,8 +171,8 @@ merge_dhclient_config (const char *iface,
 		GError *read_error = NULL;
 
 		if (!g_file_get_contents (orig_path, &orig, NULL, &read_error)) {
-			nm_log_warn (LOGD_DHCP, "(%s): error reading dhclient%s configuration %s: %s",
-			             iface, is_ip6 ? "6" : "", orig_path, read_error->message);
+			_LOGW ("error reading dhclient configuration %s: %s",
+			       orig_path, read_error->message);
 			g_error_free (read_error);
 		}
 	}
@@ -185,7 +187,7 @@ merge_dhclient_config (const char *iface,
 }
 
 static char *
-find_existing_config (const char *iface, const char *uuid, gboolean ipv6)
+find_existing_config (NMDhcpDhclient *self, const char *iface, const char *uuid, gboolean ipv6)
 {
 	char *path;
 
@@ -195,20 +197,20 @@ find_existing_config (const char *iface, const char *uuid, gboolean ipv6)
 	 */
 	if (uuid) {
 		path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", uuid);
-		nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+		_LOGD ("looking for existing config %s", path);
 		if (g_file_test (path, G_FILE_TEST_EXISTS))
 			return path;
 		g_free (path);
 	}
 
 	path = g_strdup_printf (NMCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", iface);
-	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+	_LOGD ("looking for existing config %s", path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (NMCONFDIR "/dhclient%s.conf", ipv6 ? "6" : "");
-	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+	_LOGD ("looking for existing config %s", path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
@@ -222,25 +224,25 @@ find_existing_config (const char *iface, const char *uuid, gboolean ipv6)
 	 * (including Fedora) don't even provide a default configuration file.
 	 */
 	path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s-%s.conf", ipv6 ? "6" : "", iface);
-	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+	_LOGD ("looking for existing config %s", path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (SYSCONFDIR "/dhclient%s-%s.conf", ipv6 ? "6" : "", iface);
-	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+	_LOGD ("looking for existing config %s", path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient%s.conf", ipv6 ? "6" : "");
-	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+	_LOGD ("looking for existing config %s", path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
 
 	path = g_strdup_printf (SYSCONFDIR "/dhclient%s.conf", ipv6 ? "6" : "");
-	nm_log_dbg (ipv6 ? LOGD_DHCP6 : LOGD_DHCP4, "(%s) looking for existing config %s", iface, path);
+	_LOGD ("looking for existing config %s", path);
 	if (g_file_test (path, G_FILE_TEST_EXISTS))
 		return path;
 	g_free (path);
@@ -256,7 +258,8 @@ find_existing_config (const char *iface, const char *uuid, gboolean ipv6)
  * config file along with the NM options.
  */
 static char *
-create_dhclient_config (const char *iface,
+create_dhclient_config (NMDhcpDhclient *self,
+                        const char *iface,
                         gboolean is_ip6,
                         const char *uuid,
                         GBytes *client_id,
@@ -272,26 +275,19 @@ create_dhclient_config (const char *iface,
 	g_return_val_if_fail (iface != NULL, NULL);
 
 	new = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s.conf", is_ip6 ? "6" : "", iface);
-	nm_log_dbg (is_ip6 ? LOGD_DHCP6 : LOGD_DHCP4,
-	            "(%s): creating composite dhclient config %s",
-	            iface, new);
-
-	orig = find_existing_config (iface, uuid, is_ip6);
-	if (orig) {
-		nm_log_dbg (is_ip6 ? LOGD_DHCP6 : LOGD_DHCP4,
-		            "(%s): merging existing dhclient config %s",
-		            iface, orig);
-	} else {
-		nm_log_dbg (is_ip6 ? LOGD_DHCP6 : LOGD_DHCP4,
-		            "(%s): no existing dhclient configuration to merge",
-		            iface);
-	}
+	_LOGD ("creating composite dhclient config %s", new);
+
+	orig = find_existing_config (self, iface, uuid, is_ip6);
+	if (orig)
+		_LOGD ("merging existing dhclient config %s", orig);
+	else
+		_LOGD ("no existing dhclient configuration to merge");
 
 	error = NULL;
-	success = merge_dhclient_config (iface, new, is_ip6, client_id, dhcp_anycast_addr, hostname, fqdn, orig, out_new_client_id, &error);
+	success = merge_dhclient_config (self, iface, new, is_ip6, client_id, dhcp_anycast_addr,
+			                         hostname, fqdn, orig, out_new_client_id, &error);
 	if (!success) {
-		nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient%s configuration: %s",
-		             iface, is_ip6 ? "6" : "", error->message);
+		_LOGW ("error creating dhclient configuration: %s", error->message);
 		g_error_free (error);
 	}
 
@@ -307,14 +303,14 @@ dhclient_start (NMDhcpClient *client,
                 gboolean release,
                 pid_t *out_pid)
 {
-	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	GPtrArray *argv = NULL;
 	pid_t pid;
 	GError *error = NULL;
 	const char *iface, *uuid, *system_bus_address, *dhclient_path = NULL;
 	char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL;
 	gboolean ipv6, success;
-	guint log_domain;
 	char *escaped, *preferred_leasefile_path = NULL;
 
 	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
@@ -323,11 +319,9 @@ dhclient_start (NMDhcpClient *client,
 	uuid = nm_dhcp_client_get_uuid (client);
 	ipv6 = nm_dhcp_client_get_ipv6 (client);
 
-	log_domain = ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
-
 	dhclient_path = nm_dhcp_dhclient_get_path ();
 	if (!dhclient_path) {
-		nm_log_warn (log_domain, "dhclient could not be found");
+		_LOGW ("dhclient could not be found");
 		return FALSE;
 	}
 
@@ -362,9 +356,9 @@ dhclient_start (NMDhcpClient *client,
 			priv->lease_file = g_strdup (g_file_get_path (dst));
 		} else {
 			/* Failure; just use the existing leasefile */
-			nm_log_warn (log_domain, "Failed to copy leasefile %s to %s: (%d) %s",
-			             g_file_get_path (src), g_file_get_path (dst),
-			             error->code, error->message);
+			_LOGW ("failed to copy leasefile %s to %s: %s",
+			       g_file_get_path (src), g_file_get_path (dst),
+			       error->message);
 			g_clear_error (&error);
 		}
 		g_object_unref (src);
@@ -378,10 +372,7 @@ dhclient_start (NMDhcpClient *client,
 		success = nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &error);
 		g_free (escaped);
 		if (!success) {
-			nm_log_warn (log_domain, "(%s): failed to save DUID to %s: (%d) %s.",
-			             iface, priv->lease_file,
-			             error ? error->code : -1,
-			             error && error->message ? error->message : "(unknown)");
+			_LOGW ("failed to save DUID to %s: %s", priv->lease_file, error->message);
 			g_free (pid_file);
 			return FALSE;
 		}
@@ -437,19 +428,19 @@ dhclient_start (NMDhcpClient *client,
 	g_ptr_array_add (argv, NULL);
 
 	cmd_str = g_strjoinv (" ", (gchar **) argv->pdata);
-	nm_log_dbg (log_domain, "running: %s", cmd_str);
+	_LOGD ("running: %s", cmd_str);
 	g_free (cmd_str);
 
 	if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
 	                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
 	                   nm_utils_setpgid, NULL, &pid, &error)) {
 		g_assert (pid > 0);
-		nm_log_info (log_domain, "dhclient started with pid %d", pid);
+		_LOGI ("dhclient started with pid %d", pid);
 		if (release == FALSE)
 			nm_dhcp_client_watch_child (client, pid);
 		priv->pid_file = pid_file;
 	} else {
-		nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
+		_LOGW ("dhclient failed to start: '%s'", error->message);
 		g_error_free (error);
 		g_free (pid_file);
 	}
@@ -465,7 +456,8 @@ dhclient_start (NMDhcpClient *client,
 static gboolean
 ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 {
-	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	GBytes *client_id;
 	gs_unref_bytes GBytes *new_client_id = NULL;
 	const char *iface, *uuid, *hostname, *fqdn;
@@ -477,14 +469,14 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	hostname = nm_dhcp_client_get_hostname (client);
 	fqdn = nm_dhcp_client_get_fqdn (client);
 
-	priv->conf_file = create_dhclient_config (iface, FALSE, uuid, client_id, dhcp_anycast_addr,
+	priv->conf_file = create_dhclient_config (self, iface, FALSE, uuid, client_id, dhcp_anycast_addr,
 	                                          hostname, fqdn, &new_client_id);
 	if (priv->conf_file) {
 		if (new_client_id)
 			nm_dhcp_client_set_client_id (client, new_client_id);
 		success = dhclient_start (client, NULL, NULL, FALSE, NULL);
 	} else
-		nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
+		_LOGW ("error creating dhclient configuration file");
 
 	return success;
 }
@@ -497,16 +489,17 @@ ip6_start (NMDhcpClient *client,
            NMSettingIP6ConfigPrivacy privacy,
            const GByteArray *duid)
 {
-	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	const char *iface, *uuid, *hostname;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
 	hostname = nm_dhcp_client_get_hostname (client);
 
-	priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL, NULL);
+	priv->conf_file = create_dhclient_config (self, iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL, NULL);
 	if (!priv->conf_file) {
-		nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
+		_LOGW ("error creating dhclient configuration file");
 		return FALSE;
 	}
 
@@ -516,17 +509,18 @@ ip6_start (NMDhcpClient *client,
 static void
 stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
-	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 
 	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid);
 
 	if (priv->conf_file)
 		if (remove (priv->conf_file) == -1)
-			nm_log_dbg (LOGD_DHCP, "Could not remove dhcp config file \"%s\": %d (%s)", priv->conf_file, errno, g_strerror (errno));
+			_LOGD ("could not remove dhcp config file \"%s\": %d (%s)", priv->conf_file, errno, g_strerror (errno));
 	if (priv->pid_file) {
 		if (remove (priv->pid_file) == -1)
-			nm_log_dbg (LOGD_DHCP, "Could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno));
+			_LOGD ("could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno));
 		g_free (priv->pid_file);
 		priv->pid_file = NULL;
 	}
@@ -562,7 +556,8 @@ state_changed (NMDhcpClient *client,
 static GByteArray *
 get_duid (NMDhcpClient *client)
 {
-	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	GByteArray *duid = NULL;
 	char *leasefile;
 	GError *error = NULL;
@@ -573,12 +568,12 @@ get_duid (NMDhcpClient *client)
 	                                    TRUE,
 	                                    NULL);
 	if (leasefile) {
-		nm_log_dbg (LOGD_DHCP, "Looking for DHCPv6 DUID in '%s'.", leasefile);
+		_LOGD ("looking for DUID in '%s'", leasefile);
 		duid = nm_dhcp_dhclient_read_duid (leasefile, &error);
 
 		if (error) {
-			nm_log_warn (LOGD_DHCP, "Failed to read leasefile '%s': (%d) %s",
-			             leasefile, error->code, error->message);
+			_LOGW ("failed to read leasefile '%s': %s",
+			       leasefile, error->message);
 			g_clear_error (&error);
 		}
 		g_free (leasefile);
@@ -586,13 +581,12 @@ get_duid (NMDhcpClient *client)
 
 	if (!duid && priv->def_leasefile) {
 		/* Otherwise read the default machine-wide DUID */
-		nm_log_dbg (LOGD_DHCP, "Looking for default DHCPv6 DUID in '%s'.", priv->def_leasefile);
+		_LOGD ("looking for default DUID in '%s'", priv->def_leasefile);
 		duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error);
 		if (error) {
-			nm_log_warn (LOGD_DHCP, "Failed to read leasefile '%s': (%d) %s",
-			             priv->def_leasefile,
-			             error ? error->code : -1,
-			             error ? error->message : "(unknown)");
+			_LOGW ("failed to read leasefile '%s': %s",
+			        priv->def_leasefile,
+			        error->message);
 			g_clear_error (&error);
 		}
 	}
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index 8060cc6a..8bbb6e4c 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -36,6 +36,7 @@
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-dhcp-listener.h"
+#include "nm-dhcp-client-logging.h"
 
 G_DEFINE_TYPE (NMDhcpDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
 
@@ -58,7 +59,8 @@ nm_dhcp_dhcpcd_get_path (void)
 static gboolean
 ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 {
-	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+	NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client);
+	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self);
 	GPtrArray *argv = NULL;
 	pid_t pid = -1;
 	GError *error = NULL;
@@ -77,7 +79,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 
 	dhcpcd_path = nm_dhcp_dhcpcd_get_path ();
 	if (!dhcpcd_path) {
-		nm_log_warn (LOGD_DHCP4, "dhcpcd could not be found");
+		_LOGW ("dhcpcd could not be found");
 		return FALSE;
 	}
 
@@ -134,17 +136,17 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	g_ptr_array_add (argv, NULL);
 
 	cmd_str = g_strjoinv (" ", (gchar **) argv->pdata);
-	nm_log_dbg (LOGD_DHCP4, "running: %s", cmd_str);
+	_LOGD ("running: %s", cmd_str);
 	g_free (cmd_str);
 
 	if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
 	                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
 	                   nm_utils_setpgid, NULL, &pid, &error)) {
 		g_assert (pid > 0);
-		nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid);
+		_LOGI ("dhcpcd started with pid %d", pid);
 		nm_dhcp_client_watch_child (client, pid);
 	} else {
-		nm_log_warn (LOGD_DHCP4, "dhcpcd failed to start.  error: '%s'", error->message);
+		_LOGW ("dhcpcd failed to start, error: '%s'", error->message);
 		g_error_free (error);
 	}
 
@@ -161,21 +163,24 @@ ip6_start (NMDhcpClient *client,
            NMSettingIP6ConfigPrivacy privacy,
            const GByteArray *duid)
 {
-	nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6.");
+	NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client);
+
+	_LOGW ("the dhcpcd backend does not support IPv6");
 	return FALSE;
 }
 
 static void
 stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
-	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
+	NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client);
+	NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self);
 
 	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid);
 
 	if (priv->pid_file) {
 		if (remove (priv->pid_file) == -1)
-			nm_log_dbg (LOGD_DHCP, "Could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno));
+			_LOGD ("could not remove dhcp pid file \"%s\": %d (%s)", priv->pid_file, errno, g_strerror (errno));
 	}
 
 	/* FIXME: implement release... */
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 44c3365d..fe723e96 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -113,7 +113,7 @@ find_client_desc (const char *name, GType gtype)
 }
 
 static GType
-is_client_enabled (const char *name, GError **error)
+is_client_enabled (const char *name)
 {
 	ClientDesc *desc;
 
@@ -121,9 +121,6 @@ is_client_enabled (const char *name, GError **error)
 	if (desc && (!desc->get_path_func || desc->get_path_func()))
 		return desc->gtype;
 
-	g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-	             _("'%s' support not found or not enabled."),
-	             name);
 	return G_TYPE_INVALID;
 }
 
@@ -153,28 +150,6 @@ get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
 	return NULL;
 }
 
-static GType
-get_client_type (const char *client, GError **error)
-{
-	GType client_gtype;
-
-	if (client)
-		client_gtype = is_client_enabled (client, error);
-	else {
-		/* Fallbacks */
-		client_gtype = is_client_enabled ("dhclient", NULL);
-		if (client_gtype == G_TYPE_INVALID)
-			client_gtype = is_client_enabled ("dhcpcd", NULL);
-		if (client_gtype == G_TYPE_INVALID)
-			client_gtype = is_client_enabled ("internal", NULL);
-		if (client_gtype == G_TYPE_INVALID) {
-			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-				                 _("no usable DHCP client could be found."));
-		}
-	}
-	return client_gtype;
-}
-
 static void client_state_changed (NMDhcpClient *client,
                                   NMDhcpState state,
                                   GObject *ip_config,
@@ -237,7 +212,8 @@ client_start (NMDhcpManager *self,
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
 	/* Ensure we have a usable DHCP client */
-	g_return_val_if_fail (priv->client_type != 0, NULL);
+	if (priv->client_type == G_TYPE_INVALID)
+		return NULL;
 
 	/* Kill any old client instance */
 	client = get_client_for_ifindex (self, ifindex, ipv6);
@@ -362,6 +338,7 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
                                       gboolean ipv6,
                                       guint32 default_route_metric)
 {
+	NMDhcpManagerPrivate *priv;
 	ClientDesc *desc;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
@@ -369,7 +346,11 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
 	g_return_val_if_fail (ifindex >= -1, NULL);
 	g_return_val_if_fail (uuid != NULL, NULL);
 
-	desc = find_client_desc (NULL, NM_DHCP_MANAGER_GET_PRIVATE (self)->client_type);
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+	if (priv->client_type == G_TYPE_INVALID)
+		return NULL;
+
+	desc = find_client_desc (NULL, priv->client_type);
 	if (desc && desc->get_lease_configs_func)
 		return desc->get_lease_configs_func (iface, ifindex, uuid, ipv6, default_route_metric);
 	return NULL;
@@ -385,8 +366,8 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 	NMConfig *config = nm_config_get ();
 	const char *client;
-	GError *error = NULL;
 	GSList *iter;
+	GType type = G_TYPE_INVALID;
 
 	for (iter = client_descs; iter; iter = iter->next) {
 		ClientDesc *desc = iter->data;
@@ -403,16 +384,26 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 		client = "internal";
 	}
 
-	priv->client_type = get_client_type (client, &error);
-	if (priv->client_type == G_TYPE_INVALID) {
-		nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
-		             error->message);
-	} else {
-		nm_log_dbg (LOGD_DHCP, "Using DHCP client '%s'", find_client_desc (NULL, priv->client_type)->name);
+	if (client)
+		type = is_client_enabled (client);
 
+	if (type == G_TYPE_INVALID) {
+		if (client)
+			nm_log_warn (LOGD_DHCP, "DHCP client '%s' not available", client);
+
+		type = is_client_enabled ("dhclient");
+		if (type == G_TYPE_INVALID)
+			type = is_client_enabled ("dhcpcd");
+		if (type == G_TYPE_INVALID)
+			type = is_client_enabled ("internal");
 	}
-	g_clear_error (&error);
 
+	if (type == G_TYPE_INVALID)
+		nm_log_warn (LOGD_DHCP, "No usable DHCP client found! DHCP configurations will fail");
+	else
+		nm_log_info (LOGD_DHCP, "Using DHCP client '%s'", find_client_desc (NULL, type)->name);
+
+	priv->client_type = type;
 	priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 	                                       NULL,
 	                                       (GDestroyNotify) g_object_unref);
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
index b5a3d67c..91fa8803 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.c
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -33,11 +33,11 @@
 #include "nm-dhcp-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-platform.h"
+#include "nm-dhcp-client-logging.h"
 
 #include "sd-dhcp-client.h"
 #include "sd-dhcp6-client.h"
 
-/* we use a private systemd header, thus need to include nm-sd-adapt.h. */
 #include "nm-sd-adapt.h"
 #include "dhcp-lease-internal.h"
 
@@ -53,6 +53,7 @@ typedef struct {
 	guint request_count;
 
 	gboolean privacy;
+	gboolean info_only;
 } NMDhcpSystemdPrivate;
 
 /************************************************************/
@@ -464,15 +465,15 @@ bound4_handle (NMDhcpSystemd *self)
 	GError *error = NULL;
 	int r;
 
-	nm_log_dbg (LOGD_DHCP4, "(%s): lease available", iface);
-
 	r = sd_dhcp_client_get_lease (priv->client4, &lease);
 	if (r < 0 || !lease) {
-		nm_log_warn (LOGD_DHCP4, "(%s): no lease!", iface);
+		_LOGW ("no lease!");
 		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
 		return;
 	}
 
+	_LOGD ("lease available");
+
 	options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
 	ip4_config = lease_to_ip4_config (iface,
 	                                  nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)),
@@ -498,7 +499,7 @@ bound4_handle (NMDhcpSystemd *self)
 		                          G_OBJECT (ip4_config),
 		                          options);
 	} else {
-		nm_log_warn (LOGD_DHCP4, "(%s): %s", iface, error->message);
+		_LOGW ("%s", error->message);
 		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
 		g_clear_error (&error);
 	}
@@ -512,11 +513,10 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
 {
 	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data);
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
-	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 
 	g_assert (priv->client4 == client);
 
-	nm_log_dbg (LOGD_DHCP4, "(%s): DHCPv4 client event %d", iface, event);
+	_LOGD ("client event %d", event);
 
 	switch (event) {
 	case SD_DHCP_CLIENT_EVENT_EXPIRED:
@@ -531,7 +531,7 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
 		bound4_handle (self);
 		break;
 	default:
-		nm_log_warn (LOGD_DHCP4, "(%s): unhandled DHCP event %d", iface, event);
+		_LOGW ("unhandled DHCP event %d", event);
 		break;
 	}
 }
@@ -550,7 +550,8 @@ get_arp_type (const GByteArray *hwaddr)
 static gboolean
 ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 {
-	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
+	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (client);
 	const GByteArray *hwaddr;
 	sd_dhcp_lease *lease = NULL;
@@ -571,13 +572,13 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 
 	r = sd_dhcp_client_new (&priv->client4);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to create DHCPv4 client (%d)", iface, r);
+		_LOGW ("failed to create client (%d)", r);
 		return FALSE;
 	}
 
 	r = sd_dhcp_client_attach_event (priv->client4, NULL, 0);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to attach DHCP event (%d)", iface, r);
+		_LOGW ("failed to attach event (%d)", r);
 		goto error;
 	}
 
@@ -585,7 +586,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	if (hwaddr) {
 		arp_type= get_arp_type (hwaddr);
 		if (arp_type == ARPHRD_NONE) {
-			nm_log_warn (LOGD_DHCP4, "(%s): failed to determine ARP type", iface);
+			_LOGW ("failed to determine ARP type");
 			goto error;
 		}
 
@@ -594,26 +595,26 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 		                            hwaddr->len,
 		                            arp_type);
 		if (r < 0) {
-			nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP MAC address (%d)", iface, r);
+			_LOGW ("failed to set MAC address (%d)", r);
 			goto error;
 		}
 	}
 
 	r = sd_dhcp_client_set_index (priv->client4, nm_dhcp_client_get_ifindex (client));
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP ifindex (%d)", iface, r);
+		_LOGW ("failed to set ifindex (%d)", r);
 		goto error;
 	}
 
 	r = sd_dhcp_client_set_callback (priv->client4, dhcp_event_cb, client);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP callback (%d)", iface, r);
+		_LOGW ("failed to set callback (%d)", r);
 		goto error;
 	}
 
 	r = sd_dhcp_client_set_request_broadcast (priv->client4, true);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP broadcast (%d)", iface, r);
+		_LOGW ("failed to enable broadcast mode (%d)", r);
 		goto error;
 	}
 
@@ -627,7 +628,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	if (last_addr.s_addr) {
 		r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
 		if (r < 0) {
-			nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
+			_LOGW ("failed to set last IPv4 address (%d)", r);
 			goto error;
 		}
 	}
@@ -675,7 +676,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 		free (prefix);
 
 		if (r < 0) {
-			nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP hostname (%d)", iface, r);
+			_LOGW ("failed to set DHCP hostname (%d)", r);
 			goto error;
 		}
 	}
@@ -684,14 +685,14 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	if (fqdn) {
 		r = sd_dhcp_client_set_hostname (priv->client4, fqdn);
 		if (r < 0) {
-			nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP FQDN (%d)", iface, r);
+			_LOGW ("failed to set DHCP FQDN (%d)", r);
 			goto error;
 		}
 	}
 
 	r = sd_dhcp_client_start (priv->client4);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP4, "(%s): failed to start DHCP (%d)", iface, r);
+		_LOGW ("failed to start client (%d)", r);
 		goto error;
 	}
 
@@ -706,13 +707,134 @@ error:
 	return success;
 }
 
+static NMIP6Config *
+lease_to_ip6_config (const char *iface,
+                     int ifindex,
+                     sd_dhcp6_lease *lease,
+                     GHashTable *options,
+                     gboolean log_lease,
+                     gboolean info_only,
+                     GError **error)
+{
+	struct in6_addr tmp_addr, *dns;
+	uint32_t lft_pref, lft_valid;
+	NMIP6Config *ip6_config;
+	const char *addr_str;
+	char **domains;
+	GString *str;
+	int num, i;
+	gint32 ts;
+
+	g_return_val_if_fail (lease, NULL);
+	ip6_config = nm_ip6_config_new (ifindex);
+	ts = nm_utils_get_monotonic_timestamp_s ();
+	str = g_string_sized_new (30);
+
+	/* Addresses */
+	sd_dhcp6_lease_reset_address_iter (lease);
+	while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
+		NMPlatformIP6Address address = {
+			.plen = 128,
+			.address = tmp_addr,
+			.timestamp = ts,
+			.lifetime = lft_valid,
+			.preferred = lft_pref,
+			.source = NM_IP_CONFIG_SOURCE_DHCP,
+		};
+
+		nm_ip6_config_add_address (ip6_config, &address);
+
+		addr_str = nm_utils_inet6_ntop (&tmp_addr, NULL);
+		g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str);
+
+		LOG_LEASE (LOGD_DHCP6,
+		           "  address %s",
+		           nm_platform_ip6_address_to_string (&address, NULL, 0));
+	};
+
+	if (str->len) {
+		add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str);
+		g_string_set_size (str , 0);
+	}
+
+	if (!info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) {
+		g_string_free (str, TRUE);
+		g_object_unref (ip6_config);
+		g_set_error_literal (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_FAILED,
+		                     "no address received in managed mode");
+		return NULL;
+	}
+
+	/* DNS servers */
+	num = sd_dhcp6_lease_get_dns (lease, &dns);
+	if (num > 0) {
+		for (i = 0; i < num; i++) {
+			nm_ip6_config_add_nameserver (ip6_config, &dns[i]);
+			addr_str = nm_utils_inet6_ntop (&dns[i], NULL);
+			g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str);
+			LOG_LEASE (LOGD_DHCP6, "  nameserver %s", addr_str);
+		}
+		add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str);
+		g_string_set_size (str, 0);
+	}
+
+	/* Search domains */
+	num = sd_dhcp6_lease_get_domains (lease, &domains);
+	if (num > 0) {
+		for (i = 0; i < num; i++) {
+			nm_ip6_config_add_search (ip6_config, domains[i]);
+			g_string_append_printf (str, "%s%s", str->len ? " " : "", domains[i]);
+			LOG_LEASE (LOGD_DHCP6, "  domain name '%s'", domains[i]);
+		}
+		add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str);
+		g_string_set_size (str, 0);
+	}
+
+	g_string_free (str, TRUE);
+
+	return ip6_config;
+}
+
 static void
 bound6_handle (NMDhcpSystemd *self)
 {
-	/* not yet supported... */
-	nm_log_warn (LOGD_DHCP6, "(%s): internal DHCP does not yet support DHCPv6",
-	             nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)));
-	nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
+	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
+	gs_unref_object NMIP6Config *ip6_config = NULL;
+	gs_unref_hashtable GHashTable *options = NULL;
+	gs_free_error GError *error = NULL;
+	sd_dhcp6_lease *lease;
+	int r;
+
+	r = sd_dhcp6_client_get_lease (priv->client6, &lease);
+	if (r < 0 || !lease) {
+		_LOGW (" no lease!");
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+		return;
+	}
+
+	_LOGD ("lease available");
+
+	options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+	ip6_config = lease_to_ip6_config (iface,
+	                                  nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)),
+	                                  lease,
+	                                  options,
+	                                  TRUE,
+	                                  priv->info_only,
+	                                  &error);
+
+	if (ip6_config) {
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
+		                          NM_DHCP_STATE_BOUND,
+		                          G_OBJECT (ip6_config),
+		                          options);
+	} else {
+		_LOGW ("%s", error->message);
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+	}
 }
 
 static void
@@ -720,11 +842,10 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
 {
 	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data);
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
-	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 
 	g_assert (priv->client6 == client);
 
-	nm_log_dbg (LOGD_DHCP6, "(%s): DHCPv6 client event %d", iface, event);
+	_LOGD ("client event %d", event);
 
 	switch (event) {
 	case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
@@ -738,7 +859,7 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
 		bound6_handle (self);
 		break;
 	default:
-		nm_log_warn (LOGD_DHCP6, "(%s): unhandled DHCPv6 event %d", iface, event);
+		_LOGW ("unhandled event %d", event);
 		break;
 	}
 }
@@ -751,7 +872,8 @@ ip6_start (NMDhcpClient *client,
            NMSettingIP6ConfigPrivacy privacy,
            const GByteArray *duid)
 {
-	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
+	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (client);
 	const GByteArray *hwaddr;
 	int r, i;
@@ -762,10 +884,11 @@ ip6_start (NMDhcpClient *client,
 
 	g_free (priv->lease_file);
 	priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), TRUE);
+	priv->info_only = info_only;
 
 	r = sd_dhcp6_client_new (&priv->client6);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to create DHCPv6 client (%d)", iface, r);
+		_LOGW ("failed to create client (%d)", r);
 		return FALSE;
 	}
 
@@ -777,13 +900,13 @@ ip6_start (NMDhcpClient *client,
 	                              duid->data + 2,
 	                              duid->len - 2);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to create DHCPv6 client (%d)", iface, r);
+		_LOGW ("failed to set DUID (%d)", r);
 		return FALSE;
 	}
 
 	r = sd_dhcp6_client_attach_event (priv->client6, NULL, 0);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to attach DHCP event (%d)", iface, r);
+		_LOGW ("failed to attach event (%d)", r);
 		goto error;
 	}
 
@@ -794,20 +917,20 @@ ip6_start (NMDhcpClient *client,
 		                             hwaddr->len,
 		                             get_arp_type (hwaddr));
 		if (r < 0) {
-			nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP MAC address (%d)", iface, r);
+			_LOGW ("failed to set MAC address (%d)", r);
 			goto error;
 		}
 	}
 
 	r = sd_dhcp6_client_set_index (priv->client6, nm_dhcp_client_get_ifindex (client));
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP ifindex (%d)", iface, r);
+		_LOGW ("failed to set ifindex (%d)", r);
 		goto error;
 	}
 
 	r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP callback (%d)", iface, r);
+		_LOGW ("failed to set callback (%d)", r);
 		goto error;
 	}
 
@@ -819,13 +942,13 @@ ip6_start (NMDhcpClient *client,
 
 	r = sd_dhcp6_client_set_local_address (priv->client6, ll_addr);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to set local address (%d)", iface, r);
+		_LOGW ("failed to set local address (%d)", r);
 		goto error;
 	}
 
 	r = sd_dhcp6_client_start (priv->client6);
 	if (r < 0) {
-		nm_log_warn (LOGD_DHCP6, "(%s): failed to start DHCP (%d)", iface, r);
+		_LOGW ("failed to start client (%d)", r);
 		goto error;
 	}
 
@@ -840,7 +963,8 @@ error:
 static void
 stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
-	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
+	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
+	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	int r = 0;
 
 	if (priv->client4) {
@@ -851,12 +975,8 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 		r = sd_dhcp6_client_stop (priv->client6);
 	}
 
-	if (r) {
-		nm_log_warn (priv->client6 ? LOGD_DHCP6 : LOGD_DHCP4,
-			         "(%s): failed to stop DHCP client (%d)",
-			         nm_dhcp_client_get_iface (client),
-			         r);
-	}
+	if (r)
+		_LOGW ("failed to stop client (%d)", r);
 }
 
 /***************************************************/
diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c
index 38051326..162f2dd4 100644
--- a/src/dhcp-manager/tests/test-dhcp-utils.c
+++ b/src/dhcp-manager/tests/test-dhcp-utils.c
@@ -331,7 +331,7 @@ test_dhclient_invalid_classless_routes_1 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring invalid classless static routes*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
@@ -362,7 +362,7 @@ test_dhcpcd_invalid_classless_routes_1 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring invalid classless static routes*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
@@ -395,7 +395,7 @@ test_dhclient_invalid_classless_routes_2 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring invalid classless static routes*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
@@ -428,7 +428,7 @@ test_dhcpcd_invalid_classless_routes_2 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring invalid classless static routes*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
@@ -461,7 +461,7 @@ test_dhclient_invalid_classless_routes_3 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring invalid classless static routes*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
@@ -489,7 +489,7 @@ test_dhcpcd_invalid_classless_routes_3 (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*DHCP provided invalid classless static route*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
@@ -598,7 +598,7 @@ test_invalid_escaped_domain_searches (void)
 	options = fill_table (generic_options, NULL);
 	options = fill_table (data, options);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*invalid domain search*");
 	ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0);
 	g_assert (ip4_config);
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
index 8663dfd8..460feaac 100644
--- a/src/dns-manager/nm-dns-dnsmasq.c
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -46,7 +46,20 @@ typedef struct {
 	guint32 foo;
 } NMDnsDnsmasqPrivate;
 
-/*******************************************/
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN         LOGD_DNS
+#define _NMLOG_PREFIX_NAME    "dnsmasq"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s[%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME, \
+                (self) \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
+/*****************************************************************************/
 
 static gboolean
 add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
@@ -246,7 +259,7 @@ update (NMDnsPlugin *plugin,
 
 	dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL);
 	if (!dm_binary) {
-		nm_log_warn (LOGD_DNS, "Could not find dnsmasq binary");
+		_LOGW ("could not find dnsmasq binary");
 		return FALSE;
 	}
 
@@ -283,17 +296,16 @@ update (NMDnsPlugin *plugin,
 
 	/* Write out the config file */
 	if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
-		nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s",
-		             CONFFILE,
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW ("failed to write dnsmasq config file %s: %s",
+		       CONFFILE,
+		       error->message);
 		g_clear_error (&error);
 		goto out;
 	}
 	ignored = chmod (CONFFILE, 0644);
 
-	nm_log_dbg (LOGD_DNS, "dnsmasq local caching DNS configuration:");
-	nm_log_dbg (LOGD_DNS, "%s", conf->str);
+	_LOGD ("dnsmasq local caching DNS configuration:");
+	_LOGD ("%s", conf->str);
 
 	argv[idx++] = dm_binary;
 	argv[idx++] = "--no-resolv";  /* Use only commandline */
@@ -351,18 +363,17 @@ child_quit (NMDnsPlugin *plugin, gint status)
 	if (WIFEXITED (status)) {
 		err = WEXITSTATUS (status);
 		if (err) {
-			nm_log_warn (LOGD_DNS, "dnsmasq exited with error: %s (%d)",
-			             dm_exit_code_to_msg (err),
-			             err);
+			_LOGW ("dnsmasq exited with error: %s (%d)",
+			       dm_exit_code_to_msg (err),
+			       err);
 		} else
 			failed = FALSE;
-	} else if (WIFSTOPPED (status)) {
-		nm_log_warn (LOGD_DNS, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
-	} else if (WIFSIGNALED (status)) {
-		nm_log_warn (LOGD_DNS, "dnsmasq died with signal %d", WTERMSIG (status));
-	} else {
-		nm_log_warn (LOGD_DNS, "dnsmasq died from an unknown cause");
-	}
+	} else if (WIFSTOPPED (status))
+		_LOGW ("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
+	else if (WIFSIGNALED (status))
+		_LOGW ("dnsmasq died with signal %d", WTERMSIG (status));
+	else
+		_LOGW ("dnsmasq died from an unknown cause");
 	unlink (CONFFILE);
 
 	if (failed)
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index 79d345be..ec83e600 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -100,14 +100,11 @@ NM_DEFINE_SINGLETON_INSTANCE (NMDnsManager);
             const NMDnsManager *const __self = (self); \
             \
             _nm_log (__level, _NMLOG_DOMAIN, 0, \
-                     "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                     ((__self == singleton_instance) \
-                        ? _NMLOG_PREFIX_NAME \
-                        : ({ \
-                                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
-                                __prefix; \
-                           }) \
-                     ) \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     ((!__self || __self == singleton_instance) \
+                        ? "" \
+                        : nm_sprintf_buf (__prefix, "[%p]", __self)) \
                      _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
@@ -128,6 +125,9 @@ typedef struct {
 
 	NMDnsManagerResolvConfMode resolv_conf_mode;
 	NMDnsManagerResolvConfManager rc_manager;
+	char *last_mode;
+	bool last_immutable:1;
+	bool mode_initialized:1;
 	NMDnsPlugin *plugin;
 
 	NMConfig *config;
@@ -164,6 +164,15 @@ typedef struct {
 	GPtrArray *nis_servers;
 } NMResolvConfData;
 
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager,
+	NM_UTILS_LOOKUP_DEFAULT_WARN (NULL),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE,       "none"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE,       "file"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, "resolvconf"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG,  "netconfig"),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY),
+);
+
 static void
 add_string_item (GPtrArray *array, const char *str)
 {
@@ -406,12 +415,10 @@ dispatch_netconfig (NMDnsManager *self,
 	return SR_SUCCESS;
 }
 
-static gboolean
-write_resolv_conf (FILE *f,
-                   char **searches,
-                   char **nameservers,
-                   char **options,
-                   GError **error)
+static char *
+create_resolv_conf (char **searches,
+                    char **nameservers,
+                    char **options)
 {
 	gs_free char *searches_str = NULL;
 	gs_free char *nameservers_str = NULL;
@@ -439,9 +446,9 @@ write_resolv_conf (FILE *f,
 		for (i = 0; i < num; i++) {
 			if (i == 3) {
 				g_string_append (str, "# ");
-				g_string_append (str, _("NOTE: the libc resolver may not support more than 3 nameservers."));
+				g_string_append (str, "NOTE: the libc resolver may not support more than 3 nameservers.");
 				g_string_append (str, "\n# ");
-				g_string_append (str, _("The nameservers listed below may not be recognized."));
+				g_string_append (str, "The nameservers listed below may not be recognized.");
 				g_string_append_c (str, '\n');
 			}
 
@@ -452,10 +459,18 @@ write_resolv_conf (FILE *f,
 		nameservers_str = g_string_free (str, FALSE);
 	}
 
-	if (fprintf (f, "# Generated by NetworkManager\n%s%s%s",
-	             searches_str ? searches_str : "",
-	             nameservers_str ? nameservers_str : "",
-	             options_str ? options_str : "") < 0) {
+	return g_strdup_printf ("# Generated by NetworkManager\n%s%s%s",
+	                        searches_str ?: "",
+	                        nameservers_str ?: "",
+	                        options_str ?: "");
+}
+
+static gboolean
+write_resolv_conf_contents (FILE *f,
+                            const char *content,
+                            GError **error)
+{
+	if (fprintf (f, "%s", content) < 0) {
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
@@ -467,6 +482,19 @@ write_resolv_conf (FILE *f,
 	return TRUE;
 }
 
+static gboolean
+write_resolv_conf (FILE *f,
+                   char **searches,
+                   char **nameservers,
+                   char **options,
+                   GError **error)
+{
+	gs_free char *content = NULL;
+
+	content = create_resolv_conf (searches, nameservers, options);
+	return write_resolv_conf_contents (f, content, error);
+}
+
 static SpawnResult
 dispatch_resolvconf (NMDnsManager *self,
                      char **searches,
@@ -539,18 +567,22 @@ update_resolv_conf (NMDnsManager *self,
                     char **nameservers,
                     char **options,
                     GError **error,
-                    gboolean install_etc)
+                    NMDnsManagerResolvConfManager rc_manager)
 {
 	FILE *f;
 	struct stat st;
 	gboolean success;
+	gs_free char *content = NULL;
+	SpawnResult write_file_result = SR_SUCCESS;
 
 	/* If we are not managing /etc/resolv.conf and it points to
 	 * MY_RESOLV_CONF, don't write the private DNS configuration to
 	 * MY_RESOLV_CONF otherwise we would overwrite the changes done by
 	 * some external application.
-	 */
-	if (!install_etc) {
+	 *
+	 * This is the only situation, where we don't try to update our
+	 * internal resolv.conf file. */
+	if (rc_manager == _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY) {
 		gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
 
 		if (g_strcmp0 (path, MY_RESOLV_CONF) == 0) {
@@ -560,6 +592,18 @@ update_resolv_conf (NMDnsManager *self,
 		}
 	}
 
+	content = create_resolv_conf (searches, nameservers, options);
+
+	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
+		/* we first write to /etc/resolv.conf directly. If that fails,
+		 * we still continue to write to runstatedir but remember the
+		 * error. */
+		if (!g_file_set_contents (_PATH_RESCONF, content, -1, error)) {
+			write_file_result = SR_ERROR;
+			error = NULL;
+		}
+	}
+
 	if ((f = fopen (MY_RESOLV_CONF_TMP, "w")) == NULL) {
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
@@ -570,7 +614,7 @@ update_resolv_conf (NMDnsManager *self,
 		return SR_ERROR;
 	}
 
-	success = write_resolv_conf (f, searches, nameservers, options, error);
+	success = write_resolv_conf_contents (f, content, error);
 
 	if (fclose (f) < 0) {
 		if (success) {
@@ -598,7 +642,10 @@ update_resolv_conf (NMDnsManager *self,
 		return SR_ERROR;
 	}
 
-	if (!install_etc)
+	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE)
+		return write_file_result;
+
+	if (rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE)
 		return SR_SUCCESS;
 
 	/* A symlink pointing to NM's own resolv.conf (MY_RESOLV_CONF) is always
@@ -920,7 +967,7 @@ update_dns (NMDnsManager *self,
 	nis_domain = rc.nis_domain;
 
 	/* Let any plugins do their thing first */
-	if (update && priv->plugin) {
+	if (priv->plugin) {
 		NMDnsPlugin *plugin = priv->plugin;
 		const char *plugin_name = nm_dns_plugin_get_name (plugin);
 		GSList *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL;
@@ -973,7 +1020,8 @@ update_dns (NMDnsManager *self,
 	if (update) {
 		switch (priv->rc_manager) {
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE:
-			result = update_resolv_conf (self, searches, nameservers, options, error, TRUE);
+		case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE:
+			result = update_resolv_conf (self, searches, nameservers, options, error, priv->rc_manager);
 			resolv_conf_updated = TRUE;
 			break;
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
@@ -990,7 +1038,7 @@ update_dns (NMDnsManager *self,
 		if (result == SR_NOTFOUND) {
 			_LOGD ("update-dns: program not available, writing to resolv.conf");
 			g_clear_error (error);
-			result = update_resolv_conf (self, searches, nameservers, options, error, TRUE);
+			result = update_resolv_conf (self, searches, nameservers, options, error, NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE);
 			resolv_conf_updated = TRUE;
 		}
 	}
@@ -998,7 +1046,7 @@ update_dns (NMDnsManager *self,
 	/* Unless we've already done it, update private resolv.conf in NMRUNDIR
 	   ignoring any errors */
 	if (!resolv_conf_updated)
-		update_resolv_conf (self, searches, nameservers, options, NULL, FALSE);
+		update_resolv_conf (self, searches, nameservers, options, NULL, _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY);
 
 	/* signal that resolv.conf was changed */
 	if (update && result == SR_SUCCESS)
@@ -1318,70 +1366,105 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
 
 /******************************************************************/
 
+static bool
+_get_resconf_immutable (int *immutable_cached)
+{
+	int fd, flags;
+	int immutable;
+
+	immutable = *immutable_cached;
+	if (!NM_IN_SET (immutable, FALSE, TRUE)) {
+		immutable = FALSE;
+		fd = open (_PATH_RESCONF, O_RDONLY);
+		if (fd != -1) {
+			if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1)
+				immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL);
+			close (fd);
+		}
+		*immutable_cached = immutable;
+	}
+	return immutable;
+}
+
 NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER);
 
 static void
 init_resolv_conf_mode (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	const char *mode;
-	int fd, flags;
-
-	g_clear_object (&priv->plugin);
+	const char *mode, *mode_unknown;
+	int immutable = -1;
 
 	mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config));
-	if (!g_strcmp0 (mode, "none")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
-		goto out;
+
+	if (   priv->mode_initialized
+	    && nm_streq0 (mode, priv->last_mode)
+	    && (   nm_streq0 (mode, "none")
+	        || priv->last_immutable == _get_resconf_immutable (&immutable))) {
+		/* we call init_resolv_conf_mode() on every SIGHUP to possibly reload
+		 * when either "mode" or "immutable" changed. However, we don't want to
+		 * re-create the plugin, when the paramters didn't actually change. So
+		 * detect that we would recreate the same plugin and return early. */
+		return;
 	}
 
-	fd = open (_PATH_RESCONF, O_RDONLY);
-	if (fd != -1) {
-		if (ioctl (fd, FS_IOC_GETFLAGS, &flags) == -1)
-			flags = 0;
-		close (fd);
+	priv->mode_initialized = TRUE;
+	g_free (priv->last_mode);
+	priv->last_mode = g_strdup (mode);
+	priv->last_immutable = FALSE;
+	g_clear_object (&priv->plugin);
+	priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
 
-		if (flags & FS_IMMUTABLE_FL) {
-			_LOGI ("set resolv-conf-mode: none -- " _PATH_RESCONF " is immutable");
-			priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED;
-			return;
-		}
+	if (nm_streq0 (mode, "none")) {
+		_LOGI ("%s%s", "set resolv-conf-mode: ", "none");
+		return;
 	}
 
-	if (!g_strcmp0 (mode, "dnsmasq")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
-		priv->plugin = nm_dns_dnsmasq_new ();
-	} else if (!g_strcmp0 (mode, "unbound")) {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
-		priv->plugin = nm_dns_unbound_new ();
-	} else {
-		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT;
-		if (mode && g_strcmp0 (mode, "default") != 0) {
-			_LOGW ("set resolve-conf-mode: default -- unknown configuration '%s'", mode);
-			return;
-		}
-		mode = "default";
-	}
+	priv->last_immutable = _get_resconf_immutable (&immutable);
+
+	if (NM_IN_STRSET (mode, "dnsmasq", "unbound")) {
+		if (!immutable)
+			priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY;
+		if (nm_streq (mode, "dnsmasq"))
+			priv->plugin = nm_dns_dnsmasq_new ();
+		else
+			priv->plugin = nm_dns_unbound_new ();
 
-	if (priv->plugin) {
 		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self);
 		g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self);
+
+		_NMLOG (immutable ? LOGL_WARN : LOGL_INFO,
+		        "%s%s%s%s%s%s",
+		        "set resolv-conf-mode: ",
+		        immutable ? "none" : mode,
+		        ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"",
+		        immutable ? ", resolv.conf immutable" : "");
+		return;
 	}
 
-out:
-	_LOGI ("set resolv-conf-mode: %s%s%s%s", mode,
-	       NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"", ""));
+	if (!immutable)
+		priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT;
+
+	mode_unknown = mode && !nm_streq (mode, "default") ? mode : NULL;
+	_NMLOG (mode_unknown ? LOGL_WARN : LOGL_INFO,
+	        "%s%s%s%s%s%s",
+	        "set resolv-conf-mode: ",
+	        immutable ? "none" : "default",
+	        NM_PRINT_FMT_QUOTED (mode_unknown, " -- unknown configuration '", mode_unknown, "'", ""),
+	        immutable ? ", resolv.conf immutable" : "");
 }
 
 static void
 init_resolv_conf_manager (NMDnsManager *self)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-	const char *man, *desc = "";
+	const char *man;
 
 	man = nm_config_data_get_rc_manager (nm_config_get_data (priv->config));
 	if (!g_strcmp0 (man, "none"))
 		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE;
+	else if (nm_streq0 (man, "file"))
+		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE;
 	else if (!g_strcmp0 (man, "resolvconf"))
 		priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF;
 	else if (!g_strcmp0 (man, "netconfig"))
@@ -1398,19 +1481,7 @@ init_resolv_conf_manager (NMDnsManager *self)
 			_LOGW ("unknown resolv.conf manager '%s'", man);
 	}
 
-	switch (priv->rc_manager) {
-	case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
-		desc = "resolvconf";
-		break;
-	case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG:
-		desc = "netconfig";
-		break;
-	case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE:
-		desc = "none";
-		break;
-	}
-
-	_LOGI ("using resolv.conf manager '%s'", desc);
+	_LOGI ("using resolv.conf manager '%s'", _rc_manager_to_string (priv->rc_manager));
 }
 
 static void
@@ -1422,8 +1493,15 @@ config_changed_cb (NMConfig *config,
 {
 	GError *error = NULL;
 
-	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_DNS_MODE))
+	if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_DNS_MODE |
+	                           NM_CONFIG_CHANGE_SIGHUP)) {
+		/* reload the resolv-conf mode also on SIGHUP (when DNS_MODE didn't change).
+		 * The reason is, that the configuration also depends on whether resolv.conf
+		 * is immutable, thus, without the configuration changing, we always want to
+		 * re-configure the mode. */
 		init_resolv_conf_mode (self);
+	}
+
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_RC_MANAGER))
 		init_resolv_conf_manager (self);
 
@@ -1474,6 +1552,8 @@ dispose (GObject *object)
 		g_clear_object (&priv->plugin);
 	}
 
+	g_clear_pointer (&priv->last_mode, g_free);
+
 	/* If we're quitting, leave a valid resolv.conf in place, not one
 	 * pointing to 127.0.0.1 if any plugins were active.  Thus update
 	 * DNS after disposing of all plugins.  But if we haven't done any
diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h
index 7a55f1a2..dd5c9e98 100644
--- a/src/dns-manager/nm-dns-manager.h
+++ b/src/dns-manager/nm-dns-manager.h
@@ -101,7 +101,13 @@ typedef enum {
 
 /**
  * NMDnsManagerResolvConfManager
- * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM directly writes resolv.conf
+ * @_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY: dummy-manager
+ *   to not write resolv.conf at all, only the internal file in
+ *   NM's run state directory.
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM writes resolv.conf
+ *   by symlinking it to the run state directory.
+ * @NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: Like NONE, but instead of symlinking
+ *   resolv.conf, write it as a file.
  * @NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: NM is managing resolv.conf
      through resolvconf
  * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: NM is managing resolv.conf
@@ -110,7 +116,9 @@ typedef enum {
  * NMDnsManager's management of resolv.conf
  */
 typedef enum {
+	_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY,
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE,
+	NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE,
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF,
 	NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG,
 } NMDnsManagerResolvConfManager;
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
index 8313ed13..47322d2a 100644
--- a/src/dns-manager/nm-dns-plugin.c
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -25,12 +25,12 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include "nm-core-internal.h"
+
 #include "nm-dns-plugin.h"
 #include "NetworkManagerUtils.h"
 
 typedef struct {
-	gboolean disposed;
-
 	GPid pid;
 	guint watch_id;
 	char *progname;
@@ -90,36 +90,60 @@ nm_dns_plugin_get_name (NMDnsPlugin *self)
 /********************************************/
 
 static void
+_clear_pidfile (NMDnsPlugin *self)
+{
+	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+
+	if (priv->pidfile) {
+		unlink (priv->pidfile);
+		g_free (priv->pidfile);
+		priv->pidfile = NULL;
+	}
+}
+
+static void
 kill_existing (const char *progname, const char *pidfile, const char *kill_match)
 {
-	char *contents = NULL;
 	glong pid;
-	char *proc_path = NULL;
-	char *cmdline_contents = NULL;
+	gs_free char *contents = NULL;
+	gs_free char *cmdline_contents = NULL;
+	guint64 start_time;
+	char proc_path[256];
+	gs_free_error GError *error = NULL;
 
-	if (!g_file_get_contents (pidfile, &contents, NULL, NULL))
+	if (!pidfile)
 		return;
 
-	pid = strtol (contents, NULL, 10);
-	if (pid < 1 || pid > INT_MAX)
+	if (!kill_match)
+		g_return_if_reached ();
+
+	if (!g_file_get_contents (pidfile, &contents, NULL, &error)) {
+		if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			return;
+		goto out;
+	}
+
+	pid = _nm_utils_ascii_str_to_int64 (contents, 10, 2, INT_MAX, -1);
+	if (pid == -1)
 		goto out;
 
-	proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid);
+	start_time = nm_utils_get_start_time_for_pid (pid, NULL, NULL);
+	if (start_time == 0)
+		goto out;
+
+	nm_sprintf_buf (proc_path, "/proc/%ld/cmdline", pid);
 	if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
 		goto out;
 
-	if (strstr (cmdline_contents, kill_match)) {
-		if (kill (pid, 0) == 0) {
-			nm_log_dbg (LOGD_DNS, "Killing stale %s child process %ld", progname, pid);
-			kill (pid, SIGKILL);
-		}
-		unlink (pidfile);
-	}
+	if (!strstr (cmdline_contents, kill_match))
+		goto out;
+
+	nm_utils_kill_process_sync (pid, start_time, SIGKILL, LOGD_DNS,
+	                            progname ?: "<dns-process>",
+	                            0, 0, 1000);
 
 out:
-	g_free (cmdline_contents);
-	g_free (proc_path);
-	g_free (contents);
+	unlink (pidfile);
 }
 
 static void
@@ -133,6 +157,8 @@ watch_cb (GPid pid, gint status, gpointer user_data)
 	g_free (priv->progname);
 	priv->progname = NULL;
 
+	_clear_pidfile (self);
+
 	g_signal_emit (self, signals[CHILD_QUIT], 0, status);
 }
 
@@ -153,13 +179,11 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self,
 	g_free (priv->progname);
 	priv->progname = g_path_get_basename (argv[0]);
 
-	if (pidfile) {
-		g_return_val_if_fail (kill_match != NULL, 0);
-		kill_existing (priv->progname, pidfile, kill_match);
+	kill_existing (priv->progname, pidfile, kill_match);
 
-		g_free (priv->pidfile);
-		priv->pidfile = g_strdup (pidfile);
-	}
+	g_warn_if_fail (priv->pidfile == NULL);
+	g_clear_pointer (&priv->pidfile, g_free);
+	priv->pidfile = g_strdup (pidfile);
 
 	nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname);
 	cmdline = g_strjoinv (" ", (char **) argv);
@@ -175,9 +199,8 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self,
 		nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid);
 		priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self);
 	} else {
-		nm_log_warn (LOGD_DNS, "Failed to spawn %s: (%d) %s",
-		             priv->progname, error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		nm_log_warn (LOGD_DNS, "Failed to spawn %s: %s",
+		             priv->progname, error->message);
 		g_clear_error (&error);
 	}
 
@@ -198,11 +221,7 @@ nm_dns_plugin_child_kill (NMDnsPlugin *self)
 		priv->progname = NULL;
 	}
 
-	if (priv->pidfile) {
-		unlink (priv->pidfile);
-		g_free (priv->pidfile);
-		priv->pidfile = NULL;
-	}
+	_clear_pidfile (self);
 
 	return TRUE;
 }
@@ -218,13 +237,8 @@ static void
 dispose (GObject *object)
 {
 	NMDnsPlugin *self = NM_DNS_PLUGIN (object);
-	NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
 
-	if (!priv->disposed) {
-		priv->disposed = TRUE;
-
-		nm_dns_plugin_child_kill (self);
-	}
+	nm_dns_plugin_child_kill (self);
 
 	G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object);
 }
@@ -255,21 +269,21 @@ nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
 
 	/* signals */
 	signals[FAILED] =
-		g_signal_new (NM_DNS_PLUGIN_FAILED,
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDnsPluginClass, failed),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__VOID,
-					  G_TYPE_NONE, 0);
+	    g_signal_new (NM_DNS_PLUGIN_FAILED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  G_STRUCT_OFFSET (NMDnsPluginClass, failed),
+	                  NULL, NULL,
+	                  g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
 
 	signals[CHILD_QUIT] =
-		g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
-					  G_OBJECT_CLASS_TYPE (object_class),
-					  G_SIGNAL_RUN_FIRST,
-					  G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
-					  NULL, NULL,
-					  g_cclosure_marshal_VOID__INT,
-					  G_TYPE_NONE, 1, G_TYPE_INT);
+	    g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
+	                  NULL, NULL,
+	                  g_cclosure_marshal_VOID__INT,
+	                  G_TYPE_NONE, 1, G_TYPE_INT);
 }
 
diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c
index 9e388acf..31c1db24 100644
--- a/src/dnsmasq-manager/nm-dnsmasq-manager.c
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c
@@ -34,6 +34,16 @@
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 
+#define _NMLOG_DOMAIN         LOGD_SHARING
+#define _NMLOG_PREFIX_NAME    "dnsmasq-manager"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
 typedef struct {
 	char *iface;
 	char *pidfile;
@@ -181,7 +191,7 @@ dm_exit_code (guint dm_exit_status)
 		break;
 	}
 
-	nm_log_warn (LOGD_SHARING, "dnsmasq exited with error: %s (%d)", msg, dm_exit_status);
+	_LOGW ("dnsmasq exited with error: %s (%d)", msg, dm_exit_status);
 }
 
 static void
@@ -196,11 +206,11 @@ dm_watch_cb (GPid pid, gint status, gpointer user_data)
 		if (err != 0)
 			dm_exit_code (err);
 	} else if (WIFSTOPPED (status)) {
-		nm_log_warn (LOGD_SHARING, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
+		_LOGW ("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
 	} else if (WIFSIGNALED (status)) {
-		nm_log_warn (LOGD_SHARING, "dnsmasq died with signal %d", WTERMSIG (status));
+		_LOGW ("dnsmasq died with signal %d", WTERMSIG (status));
 	} else {
-		nm_log_warn (LOGD_SHARING, "dnsmasq died from an unknown cause");
+		_LOGW ("dnsmasq died from an unknown cause");
 	}
 
 	priv->pid = 0;
@@ -269,7 +279,7 @@ create_dm_cmd_line (const char *iface,
 		                     NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_FAILED,
 		                     error_desc);
-		nm_log_warn (LOGD_SHARING, "Failed to find DHCP address ranges: %s", error_desc);
+		_LOGW ("failed to find DHCP address ranges: %s", error_desc);
 		g_free (error_desc);
 		nm_cmd_line_destroy (cmd);
 		return NULL;
@@ -361,10 +371,8 @@ nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
 
 	g_ptr_array_add (dm_cmd->array, NULL);
 
-	nm_log_info (LOGD_SHARING, "Starting dnsmasq...");
-
-	nm_log_dbg (LOGD_SHARING, "Command line: %s",
-	            (cmd_str = nm_cmd_line_to_str (dm_cmd)));
+	_LOGI ("starting dnsmasq...");
+	_LOGD ("command line: %s", (cmd_str = nm_cmd_line_to_str (dm_cmd)));
 
 	priv->pid = 0;
 	if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL,
@@ -374,7 +382,7 @@ nm_dnsmasq_manager_start (NMDnsMasqManager *manager,
 		goto out;
 	}
 
-	nm_log_dbg (LOGD_SHARING, "dnsmasq started with pid %d", priv->pid);
+	_LOGD ("dnsmasq started with pid %d", priv->pid);
 
 	priv->dm_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) dm_watch_cb, manager);
 
diff --git a/src/main-utils.c b/src/main-utils.c
index e0f254b7..86242802 100644
--- a/src/main-utils.c
+++ b/src/main-utils.c
@@ -29,6 +29,7 @@
 #include <sys/stat.h>
 #include <locale.h>
 
+#include <glib/gstdio.h>
 #include <glib-unix.h>
 #include <gmodule.h>
 
@@ -112,6 +113,31 @@ nm_main_utils_write_pidfile (const char *pidfile)
 }
 
 void
+nm_main_utils_ensure_statedir ()
+{
+	gs_free char *parent = NULL;
+	int errsv;
+
+	parent = g_path_get_dirname (NMSTATEDIR);
+
+	/* Ensure parent state directories exists */
+	if (   parent
+	    && parent[0] == '/'
+	    && parent[1] != '\0'
+	    && g_mkdir_with_parents (parent, 0755) != 0) {
+		errsv = errno;
+		fprintf (stderr, "Cannot create parents for '%s': %s", NMSTATEDIR, g_strerror (errsv));
+		exit (1);
+	}
+	/* Ensure state directory exists */
+	if (g_mkdir_with_parents (NMSTATEDIR, 0700) != 0) {
+		errsv = errno;
+		fprintf (stderr, "Cannot create '%s': %s", NMSTATEDIR, g_strerror (errsv));
+		exit (1);
+	}
+}
+
+void
 nm_main_utils_ensure_rundir ()
 {
 	/* Setup runtime directory */
@@ -178,7 +204,7 @@ void
 nm_main_utils_ensure_root ()
 {
 	if (getuid () != 0) {
-		fprintf (stderr, _("You must be root to run %s!\n"), str_if_set (g_get_prgname (), ""));
+		fprintf (stderr, _("You must be root to run %s!\n"), g_get_prgname () ?: "");
 		exit (1);
 	}
 }
diff --git a/src/main-utils.h b/src/main-utils.h
index cae97b57..10ba3135 100644
--- a/src/main-utils.h
+++ b/src/main-utils.h
@@ -27,6 +27,7 @@ void     nm_main_utils_ensure_root (void);
 
 void     nm_main_utils_setup_signals (GMainLoop *main_loop);
 
+void     nm_main_utils_ensure_statedir (void);
 void     nm_main_utils_ensure_rundir (void);
 
 gboolean nm_main_utils_write_pidfile (const char *pidfile);
diff --git a/src/main.c b/src/main.c
index 9495a9ef..4616155d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -35,9 +35,9 @@
 #include <string.h>
 #include <sys/resource.h>
 
+#include "main-utils.h"
 #include "nm-dbus-interface.h"
 #include "NetworkManagerUtils.h"
-#include "main-utils.h"
 #include "nm-manager.h"
 #include "nm-linux-platform.h"
 #include "nm-bus-manager.h"
@@ -50,6 +50,7 @@
 #include "nm-auth-manager.h"
 #include "nm-core-internal.h"
 #include "nm-exported-object.h"
+#include "nm-sd.h"
 
 #if !defined(NM_DIST_VERSION)
 # define NM_DIST_VERSION VERSION
@@ -216,8 +217,7 @@ print_config (NMConfigCmdLineOptions *config_cli)
 
 	config = nm_config_new (config_cli, NULL, &error);
 	if (config == NULL) {
-		fprintf (stderr, _("Failed to read configuration: %s\n"),
-		         (error && error->message) ? error->message : _("unknown"));
+		fprintf (stderr, _("Failed to read configuration: %s\n"), error->message);
 		return 7;
 	}
 
@@ -272,6 +272,7 @@ main (int argc, char *argv[])
 	gboolean wrote_pidfile = FALSE;
 	char *bad_domains = NULL;
 	NMConfigCmdLineOptions *config_cli;
+	guint sd_id = 0;
 
 	nm_g_type_init ();
 
@@ -308,12 +309,7 @@ main (int argc, char *argv[])
 
 	nm_main_utils_ensure_not_running_pidfile (global_opt.pidfile);
 
-	/* Ensure state directory exists */
-	if (g_mkdir_with_parents (NMSTATEDIR, 0755) != 0) {
-		fprintf (stderr, "Cannot create '%s': %s", NMSTATEDIR, strerror (errno));
-		exit (1);
-	}
-
+	nm_main_utils_ensure_statedir ();
 	nm_main_utils_ensure_rundir ();
 
 	/* When running from the build directory, determine our build directory
@@ -359,9 +355,8 @@ main (int argc, char *argv[])
 	nm_config_cmd_line_options_free (config_cli);
 	config_cli = NULL;
 	if (config == NULL) {
-		fprintf (stderr, _("Failed to read configuration: (%d) %s\n"),
-		         error ? error->code : -1,
-		         (error && error->message) ? error->message : _("unknown"));
+		fprintf (stderr, _("Failed to read configuration: %s\n"),
+		         error->message);
 		exit (1);
 	}
 
@@ -413,10 +408,9 @@ main (int argc, char *argv[])
 
 	/* Parse the state file */
 	if (!parse_state_file (global_opt.state_file, &net_enabled, &wifi_enabled, &wwan_enabled, &error)) {
-		nm_log_err (LOGD_CORE, "State file %s parsing failed: (%d) %s",
+		nm_log_err (LOGD_CORE, "State file %s parsing failed: %s",
 		            global_opt.state_file,
-		            error ? error->code : -1,
-		            (error && error->message) ? error->message : _("unknown"));
+		            error->message);
 		/* Not a hard failure */
 	}
 	g_clear_error (&error);
@@ -477,8 +471,11 @@ main (int argc, char *argv[])
 
 	success = TRUE;
 
-	if (configure_and_quit == FALSE)
+	if (configure_and_quit == FALSE) {
+		sd_id = nm_sd_event_attach_default ();
+
 		g_main_loop_run (main_loop);
+	}
 
 done:
 	nm_exported_object_class_set_quitting ();
@@ -489,5 +486,8 @@ done:
 		unlink (global_opt.pidfile);
 
 	nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
+
+	nm_clear_g_source (&sd_id);
+
 	exit (success ? 0 : 1);
 }
diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c
index 17b1d75d..acc3e4ca 100644
--- a/src/nm-activation-request.c
+++ b/src/nm-activation-request.c
@@ -303,9 +303,8 @@ nm_act_request_set_shared (NMActRequest *req, gboolean shared)
 			nm_log_info (LOGD_SHARING, "Executing: %s", cmd);
 			if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
 			                   NULL, NULL, NULL, NULL, &status, &error)) {
-				nm_log_warn (LOGD_SHARING, "Error executing command: (%d) %s",
-				             error ? error->code : -1,
-				             (error && error->message) ? error->message : "(unknown)");
+				nm_log_warn (LOGD_SHARING, "Error executing command: %s",
+				             error->message);
 				g_clear_error (&error);
 			} else if (WEXITSTATUS (status)) {
 				nm_log_warn (LOGD_SHARING, "** Command returned exit status %d.",
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index f275b3ef..e9076136 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -59,6 +59,8 @@ typedef struct {
 	NMActiveConnection *master;
 	gboolean master_ready;
 
+	NMActiveConnection *parent;
+
 	gboolean assumed;
 
 	NMAuthChain *chain;
@@ -98,6 +100,7 @@ enum {
 enum {
 	DEVICE_CHANGED,
 	DEVICE_METERED_CHANGED,
+	PARENT_ACTIVE,
 	LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -678,6 +681,69 @@ nm_active_connection_get_assumed (NMActiveConnection *self)
 
 /****************************************************************/
 
+static void unwatch_parent (NMActiveConnection *self);
+
+static void
+parent_destroyed (gpointer user_data, GObject *parent)
+{
+	NMActiveConnection *self = user_data;
+
+	unwatch_parent (self);
+	g_signal_emit (self, signals[PARENT_ACTIVE], 0, NULL);
+}
+
+static void
+parent_state_cb (NMActiveConnection *parent_ac,
+                 GParamSpec *pspec,
+                 gpointer user_data)
+{
+	NMActiveConnection *self = user_data;
+	NMActiveConnectionState parent_state = nm_active_connection_get_state (parent_ac);
+
+	if (parent_state < NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
+		return;
+
+	unwatch_parent (self);
+	g_signal_emit (self, signals[PARENT_ACTIVE], 0, parent_ac);
+}
+
+static void
+unwatch_parent (NMActiveConnection *self)
+{
+	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+
+	g_signal_handlers_disconnect_by_func (priv->parent,
+	                                      (GCallback) parent_state_cb,
+	                                      self);
+	g_object_weak_unref ((GObject *) priv->parent, parent_destroyed, self);
+	priv->parent = NULL;
+}
+
+/**
+ * nm_active_connection_set_parent:
+ * @self: the #NMActiveConnection
+ * @parent: The #NMActiveConnection that must be active before the manager
+ * can proceed progressing the device to disconnected state for us.
+ *
+ * Sets the parent connection of @self. A "parent-active" signal will be
+ * emitted when the parent connection becomes active.
+ */
+void
+nm_active_connection_set_parent (NMActiveConnection *self, NMActiveConnection *parent)
+{
+	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+
+	g_return_if_fail (priv->parent == NULL);
+	priv->parent = parent;
+	g_signal_connect (priv->parent,
+	                  "notify::" NM_ACTIVE_CONNECTION_STATE,
+	                  (GCallback) parent_state_cb,
+	                  self);
+	g_object_weak_ref ((GObject *) priv->parent, parent_destroyed, self);
+}
+
+/****************************************************************/
+
 static void
 auth_done (NMAuthChain *chain,
            GError *error,
@@ -1028,6 +1094,10 @@ dispose (GObject *object)
 		                                      self);
 	}
 	g_clear_object (&priv->master);
+
+	if (priv->parent)
+		unwatch_parent (self);
+
 	g_clear_object (&priv->subject);
 
 	G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object);
@@ -1208,6 +1278,14 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1, G_TYPE_UINT);
 
+	signals[PARENT_ACTIVE] =
+		g_signal_new (NM_ACTIVE_CONNECTION_PARENT_ACTIVE,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMActiveConnectionClass, parent_active),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1, NM_TYPE_ACTIVE_CONNECTION);
+
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ac_class),
 	                                        NMDBUS_TYPE_ACTIVE_CONNECTION_SKELETON,
 	                                        NULL);
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index a7b3d0cd..a66f8e9f 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -58,6 +58,7 @@
 /* Internal signals*/
 #define NM_ACTIVE_CONNECTION_DEVICE_CHANGED          "device-changed"
 #define NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED  "device-metered-changed"
+#define NM_ACTIVE_CONNECTION_PARENT_ACTIVE           "parent-active"
 
 struct _NMActiveConnection {
 	NMExportedObject parent;
@@ -81,6 +82,8 @@ typedef struct {
 
 	void (*device_metered_changed) (NMActiveConnection *connection,
 	                                NMMetered new_value);
+
+	void (*parent_active) (NMActiveConnection *connection);
 } NMActiveConnectionClass;
 
 guint64 nm_active_connection_version_id_get (NMActiveConnection *self);
@@ -148,6 +151,9 @@ gboolean      nm_active_connection_get_master_ready (NMActiveConnection *self);
 void          nm_active_connection_set_master (NMActiveConnection *self,
                                                NMActiveConnection *master);
 
+void          nm_active_connection_set_parent (NMActiveConnection *self,
+                                               NMActiveConnection *parent);
+
 void          nm_active_connection_set_assumed (NMActiveConnection *self,
                                                 gboolean assumed);
 
diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c
index b4cfb20b..728575f6 100644
--- a/src/nm-audit-manager.c
+++ b/src/nm-audit-manager.c
@@ -34,6 +34,15 @@
 
 #define AUDIT_LOG_LEVEL LOGL_INFO
 
+#define _NMLOG_PREFIX_NAME    "audit"
+#define _NMLOG(level, domain, ...) \
+    G_STMT_START { \
+        nm_log ((level), (domain), \
+                "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+    } G_STMT_END
+
 typedef enum {
        BACKEND_LOG    = (1 << 0),
        BACKEND_AUDITD = (1 << 1),
@@ -154,7 +163,7 @@ nm_audit_log (NMAuditManager *self, GPtrArray *fields, const char *file,
 
 	if (nm_logging_enabled (AUDIT_LOG_LEVEL, LOGD_AUDIT)) {
 		msg = build_message (fields, BACKEND_LOG);
-		_nm_log_impl (file, line, func, AUDIT_LOG_LEVEL, LOGD_AUDIT, 0, "%s", msg);
+		_NMLOG (AUDIT_LOG_LEVEL, LOGD_AUDIT, "%s", msg);
 		g_free (msg);
 	}
 }
@@ -306,17 +315,16 @@ init_auditd (NMAuditManager *self)
 	                                      NM_CONFIG_DEFAULT_LOGGING_AUDIT)) {
 		if (priv->auditd_fd < 0) {
 			priv->auditd_fd = audit_open ();
-			if (priv->auditd_fd < 0) {
-				nm_log_err (LOGD_CORE, "failed to open auditd socket: %s",
-				            strerror (errno));
-			} else
-				nm_log_dbg (LOGD_CORE, "audit socket created");
+			if (priv->auditd_fd < 0)
+				_LOGE (LOGD_CORE, "failed to open auditd socket: %s", strerror (errno));
+			else
+				_LOGD (LOGD_CORE, "socket created");
 		}
 	} else {
 		if (priv->auditd_fd >= 0) {
 			audit_close (priv->auditd_fd);
 			priv->auditd_fd = -1;
-			nm_log_dbg (LOGD_CORE, "audit socket closed");
+			_LOGD (LOGD_CORE, "socket closed");
 		}
 	}
 }
diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c
index e741546b..928e88b0 100644
--- a/src/nm-auth-utils.c
+++ b/src/nm-auth-utils.c
@@ -335,8 +335,8 @@ pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 	}
 
 	if (error) {
-		nm_log_warn (LOGD_CORE, "error requesting auth for %s: (%d) %s",
-		             call->permission, error->code, error->message);
+		nm_log_warn (LOGD_CORE, "error requesting auth for %s: %s",
+		             call->permission, error->message);
 
 		if (!call->chain->error) {
 			call->chain->error = error;
diff --git a/src/nm-bus-manager.c b/src/nm-bus-manager.c
index b6245b24..656e7dbf 100644
--- a/src/nm-bus-manager.c
+++ b/src/nm-bus-manager.c
@@ -34,6 +34,16 @@
 #include "nm-exported-object.h"
 #include "NetworkManagerUtils.h"
 
+#define _NMLOG_DOMAIN       LOGD_CORE
+#define _NMLOG_PREFIX_NAME  "bus-manager"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
 enum {
 	DBUS_CONNECTION_CHANGED = 0,
 	PRIVATE_CONNECTION_NEW,
@@ -100,7 +110,7 @@ nm_bus_manager_setup (NMBusManager *instance)
 	already_setup = TRUE;
 	singleton_instance = instance;
 	nm_singleton_instance_register ();
-	nm_log_dbg (LOGD_CORE, "setup %s singleton (%p)", "NMBusManager", singleton_instance);
+	_LOGD ("setup %s singleton (%p)", "NMBusManager", singleton_instance);
 }
 
 /**************************************************************/
@@ -176,8 +186,7 @@ private_server_closed_connection (GDBusConnection *conn,
 	CloseConnectionInfo *info;
 
 	/* Clean up after the connection */
-	nm_log_dbg (LOGD_CORE, "(%s) closed connection %p on private socket.",
-	            s->tag, conn);
+	_LOGD ("(%s) closed connection %p on private socket", s->tag, conn);
 
 	info = g_slice_new0 (CloseConnectionInfo);
 	info->connection = conn;
@@ -210,8 +219,7 @@ private_server_new_connection (GDBusServer *server,
 	g_dbus_object_manager_server_set_connection (manager, conn);
 	g_hash_table_insert (s->obj_managers, manager, sender);
 
-	nm_log_dbg (LOGD_CORE, "(%s) accepted connection %p on private socket.",
-	            s->tag, conn);
+	_LOGD ("(%s) accepted connection %p on private socket", s->tag, conn);
 
 	/* Emit this for the manager */
 	g_signal_emit (s->manager,
@@ -256,7 +264,7 @@ private_server_new (const char *path,
 	unlink (path);
 	address = g_strdup_printf ("unix:path=%s", path);
 
-	nm_log_dbg (LOGD_CORE, "(%s) creating private socket %s.", tag, address);
+	_LOGD ("(%s) creating private socket %s", tag, address);
 
 	guid = g_dbus_generate_guid ();
 	auth_observer = g_dbus_auth_observer_new ();
@@ -271,8 +279,8 @@ private_server_new (const char *path,
 	g_object_unref (auth_observer);
 
 	if (!server) {
-		nm_log_warn (LOGD_CORE, "(%s) failed to set up private socket %s: %s",
-		             tag, address, error->message);
+		_LOGW ("(%s) failed to set up private socket %s: %s",
+		       tag, address, error->message);
 		g_error_free (error);
 		g_free (address);
 		return NULL;
@@ -379,7 +387,7 @@ _bus_get_unix_pid (NMBusManager *self,
                    GError **error)
 {
 	guint32 unix_pid = G_MAXUINT32;
-	GVariant *ret;
+	gs_unref_variant GVariant *ret = NULL;
 
 	ret = _nm_dbus_proxy_call_sync (NM_BUS_MANAGER_GET_PRIVATE (self)->proxy,
 	                                "GetConnectionUnixProcessID",
@@ -403,7 +411,7 @@ _bus_get_unix_user (NMBusManager *self,
                     GError **error)
 {
 	guint32 unix_uid = G_MAXUINT32;
-	GVariant *ret;
+	gs_unref_variant GVariant *ret = NULL;
 
 	ret = _nm_dbus_proxy_call_sync (NM_BUS_MANAGER_GET_PRIVATE (self)->proxy,
 	                                "GetConnectionUnixUser",
@@ -547,8 +555,8 @@ nm_bus_manager_get_unix_user (NMBusManager *self,
 
 	/* Otherwise, a bus connection */
 	if (!_bus_get_unix_user (self, sender, out_uid, &error)) {
-		nm_log_warn (LOGD_CORE, "Failed to get unix user for dbus sender '%s': %s",
-		             sender, error->message);
+		_LOGW ("failed to get unix user for dbus sender '%s': %s",
+		       sender, error->message);
 		g_error_free (error);
 		return FALSE;
 	}
@@ -661,7 +669,7 @@ nm_bus_manager_reconnect (gpointer user_data)
 
 	if (nm_bus_manager_init_bus (self)) {
 		if (nm_bus_manager_start_service (self)) {
-			nm_log_info (LOGD_CORE, "reconnected to the system bus.");
+			_LOGI ("reconnected to the system bus");
 			g_signal_emit (self, signals[DBUS_CONNECTION_CHANGED],
 			               0, priv->connection);
 			priv->reconnect_id = 0;
@@ -695,7 +703,7 @@ closed_cb (GDBusConnection *connection,
 	NMBusManager *self = NM_BUS_MANAGER (user_data);
 
 	/* Clean up existing connection */
-	nm_log_warn (LOGD_CORE, "disconnected by the system bus.");
+	_LOGW ("disconnected by the system bus");
 
 	nm_bus_manager_cleanup (self);
 
@@ -711,7 +719,7 @@ nm_bus_manager_init_bus (NMBusManager *self)
 	GError *error = NULL;
 
 	if (priv->connection) {
-		nm_log_warn (LOGD_CORE, "DBus Manager already has a valid connection.");
+		_LOGW ("DBus Manager already has a valid connection");
 		return FALSE;
 	}
 
@@ -721,9 +729,9 @@ nm_bus_manager_init_bus (NMBusManager *self)
 		 * environments (eg, initrd) where we only want to use the private
 		 * socket.
 		 */
-		nm_log_info (LOGD_CORE, "Could not connect to the system bus (%s); only the "
-		             "private D-Bus socket will be available.",
-		             error->message);
+		_LOGI ("could not connect to the system bus (%s); only the "
+		       "private D-Bus socket will be available",
+		       error->message);
 		g_error_free (error);
 		return FALSE;
 	}
@@ -742,9 +750,9 @@ nm_bus_manager_init_bus (NMBusManager *self)
 	                                     NULL, &error);
 	if (!priv->proxy) {
 		g_clear_object (&priv->connection);
-		nm_log_warn (LOGD_CORE, "Could not create org.freedesktop.DBus proxy (%s); only the "
-		             "private D-Bus socket will be available.",
-		             error->message);
+		_LOGW ("could not create org.freedesktop.DBus proxy (%s); only the "
+		       "private D-Bus socket will be available",
+		       error->message);
 		g_error_free (error);
 		return FALSE;
 	}
@@ -761,7 +769,7 @@ gboolean
 nm_bus_manager_start_service (NMBusManager *self)
 {
 	NMBusManagerPrivate *priv;
-	GVariant *ret;
+	gs_unref_variant GVariant *ret = NULL;
 	int result;
 	GError *err = NULL;
 
@@ -770,7 +778,7 @@ nm_bus_manager_start_service (NMBusManager *self)
 	priv = NM_BUS_MANAGER_GET_PRIVATE (self);
 
 	if (priv->started) {
-		nm_log_err (LOGD_CORE, "Service has already started.");
+		_LOGE ("service has already started");
 		return FALSE;
 	}
 
@@ -787,8 +795,7 @@ nm_bus_manager_start_service (NMBusManager *self)
 	                                G_DBUS_CALL_FLAGS_NONE, -1,
 	                                NULL, &err);
 	if (!ret) {
-		nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service.\n"
-		            "  Error: '%s'", err->message);
+		_LOGE ("could not acquire the NetworkManager service: '%s'", err->message);
 		g_error_free (err);
 		return FALSE;
 	}
@@ -796,7 +803,7 @@ nm_bus_manager_start_service (NMBusManager *self)
 	g_variant_get (ret, "(u)", &result);
 
 	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
-		nm_log_err (LOGD_CORE, "Could not acquire the NetworkManager service as it is already taken.");
+		_LOGE ("could not acquire the NetworkManager service as it is already taken");
 		return FALSE;
 	}
 
@@ -940,8 +947,8 @@ nm_bus_manager_new_proxy (NMBusManager *self,
 	                        "g-interface-name", iface,
 	                        NULL);
 	if (!proxy) {
-		nm_log_warn (LOGD_CORE, "Could not create proxy for %s on connection %s: %s",
-		             iface, name, error->message);
+		_LOGW ("could not create proxy for %s on connection %s: %s",
+		       iface, name, error->message);
 		g_error_free (error);
 	}
 	return proxy;
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index a7d85c29..b98b6e8a 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -109,39 +109,6 @@ _nm_utils_set_testing (NMUtilsTestFlags flags)
 
 /*****************************************************************************/
 
-G_DEFINE_QUARK (nm-utils-error-quark, nm_utils_error)
-
-void
-nm_utils_error_set_cancelled (GError **error,
-                              gboolean is_disposing,
-                              const char *instance_name)
-{
-	if (is_disposing) {
-		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING,
-		             "Disposing %s instance",
-		             instance_name && *instance_name ? instance_name : "source");
-	} else {
-		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
-		                     "Request cancelled");
-	}
-}
-
-gboolean
-nm_utils_error_is_cancelled (GError *error,
-                             gboolean consider_is_disposing)
-{
-	if (error) {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			return TRUE;
-		if (   consider_is_disposing
-		    && g_error_matches (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING))
-			return TRUE;
-	}
-	return FALSE;
-}
-
-/*****************************************************************************/
-
 static GSList *_singletons = NULL;
 static gboolean _singletons_shutdown = FALSE;
 
@@ -454,10 +421,11 @@ nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *
 		nm_log (llevel, LOGD_CORE, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message);
 		g_propagate_error (error, local);
 		return -1;
-	} else if (exit_status != 0)
+	} else if (exit_status != 0) {
 		nm_log (llevel, LOGD_CORE, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status,
 		        std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "",
 		        std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : "");
+	}
 
 	return exit_status;
 }
@@ -1008,7 +976,6 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, NMLogDomain
 
 	g_return_if_fail (pid > 0);
 	g_return_if_fail (log_name != NULL);
-	g_return_if_fail (wait_before_kill_msec > 0);
 
 	start_time0 = nm_utils_get_start_time_for_pid (pid, &p_state, NULL);
 	if (start_time0 == 0) {
@@ -1046,12 +1013,12 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, NMLogDomain
 		return;
 	}
 
-	/* wait for the process to terminated... */
+	/* wait for the process to terminate... */
 
 	wait_start_us = nm_utils_get_monotonic_timestamp_us ();
 
 	sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec);
-	if (sig != SIGKILL)
+	if (sig != SIGKILL && wait_before_kill_msec)
 		wait_until_sigkill = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L);
 	else
 		wait_until_sigkill = 0;
@@ -2507,8 +2474,8 @@ _get_property_path (const char *ifname,
 	static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
 	int len;
 
-	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
-	property = ASSERT_VALID_PATH_COMPONENT (property);
+	ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname);
+	property = NM_ASSERT_VALID_PATH_COMPONENT (property);
 
 	len = g_snprintf (path,
 	                  sizeof (path),
@@ -2573,7 +2540,7 @@ nm_utils_is_valid_path_component (const char *name)
 }
 
 const char *
-ASSERT_VALID_PATH_COMPONENT (const char *name)
+NM_ASSERT_VALID_PATH_COMPONENT (const char *name)
 {
 	if (G_LIKELY (nm_utils_is_valid_path_component (name)))
 		return name;
@@ -2941,4 +2908,113 @@ nm_utils_parse_debug_string (const char *string,
 	return result;
 }
 
+/*****************************************************************************/
+
+void
+nm_utils_ifname_cpy (char *dst, const char *name)
+{
+	g_return_if_fail (dst);
+	g_return_if_fail (name && name[0]);
+
+	nm_assert (nm_utils_iface_valid_name (name));
+
+	if (g_strlcpy (dst, name, IFNAMSIZ) >= IFNAMSIZ)
+		g_return_if_reached ();
+}
+
+/*****************************************************************************/
+
+#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
+#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
+
+gboolean
+nm_utils_ip4_address_is_link_local (in_addr_t addr)
+{
+	return (addr & IPV4LL_NETMASK) == IPV4LL_NETWORK;
+}
+
+/*****************************************************************************/
+
+/**
+ * Takes a pair @timestamp and @duration, and returns the remaining duration based
+ * on the new timestamp @now.
+ */
+guint32
+nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
+                                               guint32 duration,
+                                               gint32 now)
+{
+	gint64 t;
+
+	nm_assert (now >= 0);
+
+	if (duration == NM_PLATFORM_LIFETIME_PERMANENT)
+		return NM_PLATFORM_LIFETIME_PERMANENT;
+
+	if (timestamp == 0) {
+		/* if the @timestamp is zero, assume it was just left unset and that the relative
+		 * @duration starts counting from @now. This is convenient to construct an address
+		 * and print it in nm_platform_ip4_address_to_string().
+		 *
+		 * In general it does not make sense to set the @duration without anchoring at
+		 * @timestamp because you don't know the absolute expiration time when looking
+		 * at the address at a later moment. */
+		timestamp = now;
+	}
+
+	/* For timestamp > now, just accept it and calculate the expected(?) result. */
+	t = (gint64) timestamp + (gint64) duration - (gint64) now;
+
+	if (t <= 0)
+		return 0;
+	if (t >= NM_PLATFORM_LIFETIME_PERMANENT)
+		return NM_PLATFORM_LIFETIME_PERMANENT - 1;
+	return t;
+}
+
+gboolean
+nm_utils_lifetime_get (guint32 timestamp,
+                       guint32 lifetime,
+                       guint32 preferred,
+                       gint32 now,
+                       guint32 *out_lifetime,
+                       guint32 *out_preferred)
+{
+	guint32 t_lifetime, t_preferred;
+
+	nm_assert (now >= 0);
+
+	if (lifetime == 0) {
+		*out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
+		*out_preferred = NM_PLATFORM_LIFETIME_PERMANENT;
+
+		/* We treat lifetime==0 as permanent addresses to allow easy creation of such addresses
+		 * (without requiring to set the lifetime fields to NM_PLATFORM_LIFETIME_PERMANENT).
+		 * In that case we also expect that the other fields (timestamp and preferred) are left unset. */
+		g_return_val_if_fail (timestamp == 0 && preferred == 0, TRUE);
+	} else {
+		if (now <= 0)
+			now = nm_utils_get_monotonic_timestamp_s ();
+		t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
+		if (!t_lifetime) {
+			*out_lifetime = 0;
+			*out_preferred = 0;
+			return FALSE;
+		}
+		t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
+
+		*out_lifetime = t_lifetime;
+		*out_preferred = MIN (t_preferred, t_lifetime);
+
+		/* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
+		 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
+		 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
+		 */
+		g_return_val_if_fail (   timestamp != 0
+		                      || (   lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
+		                          && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
+		g_return_val_if_fail (t_preferred <= t_lifetime, TRUE);
+	}
+	return TRUE;
+}
 
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index 7a69fb19..f77b43e2 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -30,6 +30,8 @@
 
 /*****************************************************************************/
 
+#define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32
+
 #define NM_DEFINE_SINGLETON_INSTANCE(TYPE) \
 static TYPE *singleton_instance
 
@@ -89,31 +91,6 @@ GETTER (void) \
 
 /*****************************************************************************/
 
-/**
- * NMUtilsError:
- * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error
- * @NM_UTILS_ERROR_CANCELLED_DISPOSING: when disposing an object that has
- *   pending aynchronous operations, the operation is cancelled with this
- *   error reason. Depending on the usage, this might indicate a bug because
- *   usually the target object should stay alive as long as there are pending
- *   operations.
- */
-typedef enum {
-	NM_UTILS_ERROR_UNKNOWN = 0,                 /*< nick=Unknown >*/
-	NM_UTILS_ERROR_CANCELLED_DISPOSING,         /*< nick=CancelledDisposing >*/
-} NMUtilsError;
-
-#define NM_UTILS_ERROR (nm_utils_error_quark ())
-GQuark nm_utils_error_quark (void);
-
-void nm_utils_error_set_cancelled (GError **error,
-                                   gboolean is_disposing,
-                                   const char *instance_name);
-gboolean nm_utils_error_is_cancelled (GError *error,
-                                      gboolean consider_is_disposing);
-
-/*****************************************************************************/
-
 gint nm_utils_ascii_str_to_bool (const char *str,
                                  gint default_value);
 
@@ -143,22 +120,6 @@ int nm_spawn_process (const char *args, GError **error);
 
 int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED;
 
-/**
- * str_if_set:
- * @str: input string that will be returned if @str is not %NULL
- * @fallback: if @str is %NULL, return @fallback instead
- *
- * This utility function is useful when printing a string to avoid passing
- * %NULL. E.g. printf ("%s", str_if_set (get_string(), "(none)"));
- *
- * Returns: either @str or @fallback, depending on whether @str is %NULL.
- */
-static inline const char *
-str_if_set (const char *str, const char *fallback)
-{
-	return str ? str : fallback;
-}
-
 guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
 
 void nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_domain,
@@ -331,6 +292,9 @@ int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnect
 void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix);
 
 #define NM_UTILS_NS_PER_SECOND  ((gint64) 1000000000)
+#define NM_UTILS_NS_PER_MSEC    ((gint64) 1000000)
+#define NM_UTILS_NS_TO_MSEC_CEIL(nsec)      (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC)
+
 gint64 nm_utils_get_monotonic_timestamp_ns (void);
 gint64 nm_utils_get_monotonic_timestamp_us (void);
 gint64 nm_utils_get_monotonic_timestamp_ms (void);
@@ -338,7 +302,7 @@ gint32 nm_utils_get_monotonic_timestamp_s (void);
 gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns);
 
 gboolean    nm_utils_is_valid_path_component (const char *name);
-const char *ASSERT_VALID_PATH_COMPONENT (const char *name);
+const char *NM_ASSERT_VALID_PATH_COMPONENT (const char *name);
 const char *nm_utils_ip6_property_path (const char *ifname, const char *property);
 const char *nm_utils_ip4_property_path (const char *ifname, const char *property);
 
@@ -412,4 +376,19 @@ guint nm_utils_parse_debug_string (const char *string,
                                    const GDebugKey *keys,
                                    guint nkeys);
 
+void nm_utils_ifname_cpy (char *dst, const char *name);
+
+guint32 nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
+                                                       guint32 duration,
+                                                       gint32 now);
+
+gboolean nm_utils_lifetime_get (guint32 timestamp,
+                                guint32 lifetime,
+                                guint32 preferred,
+                                gint32 now,
+                                guint32 *out_lifetime,
+                                guint32 *out_preferred);
+
+gboolean nm_utils_ip4_address_is_link_local (in_addr_t addr);
+
 #endif /* __NM_CORE_UTILS_H__ */
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c
index e2c4eefe..3ee634a7 100644
--- a/src/nm-default-route-manager.c
+++ b/src/nm-default-route-manager.c
@@ -58,6 +58,10 @@ typedef struct {
 
 G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT)
 
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PLATFORM,
+);
+
 NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, NM_TYPE_DEFAULT_ROUTE_MANAGER);
 
 #define _NMLOG_PREFIX_NAME   "default-route"
@@ -283,7 +287,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 		return FALSE;
 
 	if (vtable->vt->is_ip4) {
-		success = nm_platform_ip4_route_add (NM_PLATFORM_GET,
+		success = nm_platform_ip4_route_add (priv->platform,
 		                                     entry->route.rx.ifindex,
 		                                     entry->route.rx.source,
 		                                     0,
@@ -293,7 +297,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 		                                     entry->effective_metric,
 		                                     entry->route.rx.mss);
 	} else {
-		success = nm_platform_ip6_route_add (NM_PLATFORM_GET,
+		success = nm_platform_ip6_route_add (priv->platform,
 		                                     entry->route.rx.ifindex,
 		                                     entry->route.rx.source,
 		                                     in6addr_any,
@@ -319,7 +323,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self,
 	gboolean changed = FALSE;
 
 	/* prune all other default routes from this device. */
-	routes = vtable->vt->route_get_all (NM_PLATFORM_GET, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
+	routes = vtable->vt->route_get_all (priv->platform, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
 
 	for (i = 0; i < routes->len; i++) {
 		const NMPlatformIPRoute *route;
@@ -351,7 +355,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self,
 		 */
 		if (   !entry
 		    && (has_ifindex_synced || ifindex_to_flush == route->ifindex)) {
-			vtable->vt->route_delete_default (NM_PLATFORM_GET, route->ifindex, route->metric);
+			vtable->vt->route_delete_default (priv->platform, route->ifindex, route->metric);
 			changed = TRUE;
 		}
 	}
@@ -505,7 +509,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
 
 	entries = vtable->get_entries (priv);
 
-	routes = vtable->vt->route_get_all (NM_PLATFORM_GET, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
+	routes = vtable->vt->route_get_all (priv->platform, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
 
 	assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes);
 
@@ -1381,20 +1385,54 @@ _platform_changed_cb (NMPlatform *platform,
 /***********************************************************************************/
 
 static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object);
+	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_PLATFORM:
+		/* construct-only */
+		priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
+		if (!priv->platform)
+			g_return_if_reached ();
+		g_object_ref (priv->platform);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
 nm_default_route_manager_init (NMDefaultRouteManager *self)
 {
+}
+
+static void
+constructed (GObject *object)
+{
+	NMDefaultRouteManager *self = NM_DEFAULT_ROUTE_MANAGER (object);
 	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
 
 	priv->entries_ip4 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free);
 	priv->entries_ip6 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free);
 
-	priv->platform = g_object_ref (NM_PLATFORM_GET);
 	g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self);
 	g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self);
 	g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self);
 	g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self);
 }
 
+NMDefaultRouteManager *
+nm_default_route_manager_new (NMPlatform *platform)
+{
+	return g_object_new (NM_TYPE_DEFAULT_ROUTE_MANAGER,
+	                     NM_DEFAULT_ROUTE_MANAGER_PLATFORM, platform,
+	                     NULL);
+}
+
 static void
 dispose (GObject *object)
 {
@@ -1437,6 +1475,17 @@ nm_default_route_manager_class_init (NMDefaultRouteManagerClass *klass)
 	g_type_class_add_private (klass, sizeof (NMDefaultRouteManagerPrivate));
 
 	/* virtual methods */
+	object_class->constructed = constructed;
 	object_class->dispose = dispose;
+	object_class->set_property = set_property;
+
+	obj_properties[PROP_PLATFORM] =
+	    g_param_spec_object (NM_DEFAULT_ROUTE_MANAGER_PLATFORM, "", "",
+	                         NM_TYPE_PLATFORM,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 }
 
diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h
index 2ee4b6d6..ca92814c 100644
--- a/src/nm-default-route-manager.h
+++ b/src/nm-default-route-manager.h
@@ -33,7 +33,7 @@
 #define NM_IS_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER))
 #define NM_DEFAULT_ROUTE_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass))
 
-
+#define NM_DEFAULT_ROUTE_MANAGER_PLATFORM "platform"
 
 struct _NMDefaultRouteManager {
 	GObject parent;
@@ -46,6 +46,7 @@ typedef struct {
 GType nm_default_route_manager_get_type (void);
 
 NMDefaultRouteManager *nm_default_route_manager_get (void);
+NMDefaultRouteManager *nm_default_route_manager_new (NMPlatform *platform);
 
 void nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source);
 void nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source);
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index c0238fc5..4959ed5d 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -39,6 +39,16 @@
 
 #define CALL_TIMEOUT (1000 * 60 * 10)  /* 10 minutes for all scripts */
 
+#define _NMLOG_DOMAIN         LOGD_DISPATCH
+#define _NMLOG_PREFIX_NAME    "dispatcher"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
 static GDBusProxy *dispatcher_proxy;
 static GHashTable *requests = NULL;
 
@@ -332,8 +342,7 @@ dispatcher_results_process (guint request_id, DispatcherAction action, GVariantI
 	g_return_if_fail (results != NULL);
 
 	if (g_variant_iter_n_children (results) == 0) {
-		nm_log_dbg (LOGD_DISPATCH, "(%u) succeeded but no scripts invoked",
-		            request_id);
+		_LOGD ("(%u) succeeded but no scripts invoked", request_id);
 		return;
 	}
 
@@ -353,15 +362,16 @@ dispatcher_results_process (guint request_id, DispatcherAction action, GVariantI
 			script_validation_msg = " (unexpected path)";
 
 		if (result == DISPATCH_RESULT_SUCCESS) {
-			nm_log_dbg (LOGD_DISPATCH, "(%u) %s succeeded%s",
-			            request_id,
-			            script, script_validation_msg);
+			_LOGD ("(%u) %s succeeded%s",
+			       request_id,
+			       script, script_validation_msg);
 		} else {
-			nm_log_warn (LOGD_DISPATCH, "(%u) %s failed (%s): %s%s",
-			             request_id,
-			             script,
-			             dispatch_result_to_string (result),
-			             err, script_validation_msg);
+			_LOGW ("(%u) %s failed (%s): %s%s",
+			       request_id,
+			       script,
+			       dispatch_result_to_string (result),
+			       err,
+			       script_validation_msg);
 		}
 	}
 }
@@ -385,11 +395,11 @@ dispatcher_done_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 	} else {
 		if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) {
 			g_dbus_error_strip_remote_error (error);
-			nm_log_warn (LOGD_DISPATCH, "(%u) failed to call dispatcher scripts: %s",
-			             info->request_id, error->message);
+			_LOGW ("(%u) failed to call dispatcher scripts: %s",
+			       info->request_id, error->message);
 		} else {
-			nm_log_dbg (LOGD_DISPATCH, "(%u) failed to call dispatcher scripts: %s",
-			            info->request_id, error->message);
+			_LOGD ("(%u) failed to call dispatcher scripts: %s",
+			       info->request_id, error->message);
 		}
 		g_clear_error (&error);
 	}
@@ -474,21 +484,21 @@ _dispatcher_call (DispatcherAction action,
 
 	/* All actions except 'hostname' require a device */
 	if (action == DISPATCHER_ACTION_HOSTNAME) {
-		nm_log_dbg (LOGD_DISPATCH, "(%u) dispatching action '%s'%s",
-		            reqid, action_to_string (action),
-		            blocking
-		                ? " (blocking)"
-		                : (callback ? " (with callback)" : ""));
+		_LOGD ("(%u) dispatching action '%s'%s",
+		       reqid, action_to_string (action),
+		       blocking
+		           ? " (blocking)"
+		           : (callback ? " (with callback)" : ""));
 	} else {
 		g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
 
-		nm_log_dbg (LOGD_DISPATCH, "(%u) (%s) dispatching action '%s'%s",
-		            reqid,
-		            vpn_iface ? vpn_iface : nm_device_get_iface (device),
-		            action_to_string (action),
-		            blocking
-		                ? " (blocking)"
-		                : (callback ? " (with callback)" : ""));
+		_LOGD ("(%u) (%s) dispatching action '%s'%s",
+		       reqid,
+		       vpn_iface ? vpn_iface : nm_device_get_iface (device),
+		       action_to_string (action),
+		       blocking
+		           ? " (blocking)"
+		           : (callback ? " (with callback)" : ""));
 	}
 
 	if (!_get_monitor_by_action(action)->has_scripts) {
@@ -499,9 +509,9 @@ _dispatcher_call (DispatcherAction action,
 			info->callback = callback;
 			info->user_data = user_data;
 			info->idle_id = g_idle_add (dispatcher_idle_cb, info);
-			nm_log_dbg (LOGD_DISPATCH, "(%u) simulate request; no scripts in %s",  reqid, _get_monitor_by_action(action)->dir);
+			_LOGD ("(%u) simulate request; no scripts in %s",  reqid, _get_monitor_by_action(action)->dir);
 		} else
-			nm_log_dbg (LOGD_DISPATCH, "(%u) ignoring request; no scripts in %s", reqid, _get_monitor_by_action(action)->dir);
+			_LOGD ("(%u) ignoring request; no scripts in %s", reqid, _get_monitor_by_action(action)->dir);
 		success = TRUE;
 		goto done;
 	}
@@ -592,7 +602,7 @@ _dispatcher_call (DispatcherAction action,
 			success = TRUE;
 		} else {
 			g_dbus_error_strip_remote_error (error);
-			nm_log_warn (LOGD_DISPATCH, "(%u) failed: %s", reqid, error->message);
+			_LOGW ("(%u) failed: %s", reqid, error->message);
 			g_clear_error (&error);
 			success = FALSE;
 		}
@@ -764,8 +774,7 @@ nm_dispatcher_call_cancel (guint call_id)
 	g_return_if_fail (info);
 
 	if (info && info->callback) {
-		nm_log_dbg (LOGD_DISPATCH, "(%u) cancelling dispatcher callback action",
-		            call_id);
+		_LOGD ("(%u) cancelling dispatcher callback action", call_id);
 		info->callback = NULL;
 	}
 }
@@ -797,19 +806,19 @@ dispatcher_dir_changed (GFileMonitor *monitor,
 		errsv = errno;
 		g_dir_close (dir);
 		if (item->has_scripts)
-			nm_log_dbg (LOGD_DISPATCH, "dispatcher: %s script directory '%s' has scripts", item->description, item->dir);
+			_LOGD ("%s script directory '%s' has scripts", item->description, item->dir);
 		else if (errsv == 0)
-			nm_log_dbg (LOGD_DISPATCH, "dispatcher: %s script directory '%s' has no scripts", item->description, item->dir);
+			_LOGD ("%s script directory '%s' has no scripts", item->description, item->dir);
 		else {
-			nm_log_dbg (LOGD_DISPATCH, "dispatcher: %s script directory '%s' error reading (%s)", item->description, item->dir, strerror (errsv));
+			_LOGD ("%s script directory '%s' error reading (%s)", item->description, item->dir, strerror (errsv));
 			item->has_scripts = TRUE;
 		}
 	} else {
 		if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
-			nm_log_dbg (LOGD_DISPATCH, "dispatcher: %s script directory '%s' does not exist", item->description, item->dir);
+			_LOGD ("%s script directory '%s' does not exist", item->description, item->dir);
 			item->has_scripts = FALSE;
 		} else {
-			nm_log_dbg (LOGD_DISPATCH, "dispatcher: %s script directory '%s' error (%s)", item->description, item->dir, error->message);
+			_LOGD ("%s script directory '%s' error (%s)", item->description, item->dir, error->message);
 			item->has_scripts = TRUE;
 		}
 		g_error_free (error);
@@ -843,7 +852,7 @@ nm_dispatcher_init (void)
 	                                                  NM_DISPATCHER_DBUS_INTERFACE,
 	                                                  NULL, &error);
 	if (!dispatcher_proxy) {
-		nm_log_err (LOGD_DISPATCH, "could not get dispatcher proxy! %s", error->message);
+		_LOGE ("could not get dispatcher proxy! %s", error->message);
 		g_clear_error (&error);
 	}
 }
diff --git a/src/nm-enum-types.c b/src/nm-enum-types.c
index 66d54bdf..66d6c9b3 100644
--- a/src/nm-enum-types.c
+++ b/src/nm-enum-types.c
@@ -27,7 +27,6 @@
 #include "nm-device-ethernet-utils.h" 
 #include "nm-device-factory.h" 
 #include "nm-device-generic.h" 
-#include "nm-device-logging.h" 
 #include "nm-dhcp-client.h" 
 #include "nm-dhcp-utils.h" 
 #include "nm-dhcp-listener.h" 
@@ -39,6 +38,7 @@
 #include "nm-dns-utils.h" 
 #include "nm-dnsmasq-manager.h" 
 #include "nm-dnsmasq-utils.h" 
+#include "nmp-netns.h" 
 #include "nm-fake-platform.h" 
 #include "nm-linux-platform.h" 
 #include "nm-platform.h" 
@@ -215,9 +215,11 @@ nm_dns_manager_resolv_conf_manager_get_type (void)
   if (g_once_init_enter (&g_define_type_id__volatile))
     {
       static const GEnumValue values[] = {
-        { NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE, "NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE", "none" },
-        { NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, "NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF", "resolvconf" },
-        { NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, "NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG", "netconfig" },
+        { _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY, "_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY", "-nm-dns-manager-resolv-conf-man-internal-only" },
+        { NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE, "NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE", "nm-dns-manager-resolv-conf-man-none" },
+        { NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE, "NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE", "nm-dns-manager-resolv-conf-man-file" },
+        { NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, "NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF", "nm-dns-manager-resolv-conf-man-resolvconf" },
+        { NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, "NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG", "nm-dns-manager-resolv-conf-man-netconfig" },
         { 0, NULL, NULL }
       };
       GType g_define_type_id =
@@ -744,25 +746,6 @@ nm_ip_config_merge_flags_get_type (void)
   return g_define_type_id__volatile;
 }
 GType
-nm_utils_error_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const GEnumValue values[] = {
-        { NM_UTILS_ERROR_UNKNOWN, "NM_UTILS_ERROR_UNKNOWN", "Unknown" },
-        { NM_UTILS_ERROR_CANCELLED_DISPOSING, "NM_UTILS_ERROR_CANCELLED_DISPOSING", "CancelledDisposing" },
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_enum_register_static (g_intern_static_string ("NMUtilsError"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-GType
 nm_match_spec_match_type_get_type (void)
 {
   static volatile gsize g_define_type_id__volatile = 0;
diff --git a/src/nm-enum-types.h b/src/nm-enum-types.h
index 6bfc3ebc..8fe52e19 100644
--- a/src/nm-enum-types.h
+++ b/src/nm-enum-types.h
@@ -64,8 +64,6 @@ GType nm_pobject_type_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_POBJECT_TYPE (nm_pobject_type_get_type ())
 GType nm_ip_config_merge_flags_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_IP_CONFIG_MERGE_FLAGS (nm_ip_config_merge_flags_get_type ())
-GType nm_utils_error_get_type (void) G_GNUC_CONST;
-#define NM_TYPE_UTILS_ERROR (nm_utils_error_get_type ())
 GType nm_match_spec_match_type_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_MATCH_SPEC_MATCH_TYPE (nm_match_spec_match_type_get_type ())
 GType nm_utils_test_flags_get_type (void) G_GNUC_CONST;
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index cc59835f..5a697537 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -36,14 +36,15 @@
  * Forward declare if_nametoindex. */
 extern unsigned int if_nametoindex (const char *__ifname);
 
+#include "main-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-linux-platform.h"
 #include "nm-dhcp-manager.h"
-#include "main-utils.h"
 #include "nm-rdisc.h"
 #include "nm-lndp-rdisc.h"
 #include "nm-utils.h"
 #include "nm-setting-ip6-config.h"
+#include "nm-sd.h"
 
 #if !defined(NM_DIST_VERSION)
 # define NM_DIST_VERSION VERSION
@@ -130,9 +131,8 @@ dhcp4_state_changed (NMDhcpClient *client,
 static void
 rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
 {
-	static NMIP6Config *last_config = NULL;
+	static NMIP6Config *rdisc_config = NULL;
 	NMIP6Config *existing;
-	NMIP6Config *ip6_config;
 	static int system_support = -1;
 	guint32 ifa_flags = 0x00;
 	int i;
@@ -157,21 +157,25 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 		ifa_flags |= IFA_F_MANAGETEMPADDR;
 	}
 
-	ip6_config = nm_ip6_config_new (ifindex);
+	existing = nm_ip6_config_capture (ifindex, FALSE, global_opt.tempaddr);
+	if (rdisc_config)
+		nm_ip6_config_subtract (existing, rdisc_config);
+	else
+		rdisc_config = nm_ip6_config_new (ifindex);
 
 	if (changed & NM_RDISC_CONFIG_GATEWAYS) {
 		/* Use the first gateway as ordered in router discovery cache. */
 		if (rdisc->gateways->len) {
 			NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
 
-			nm_ip6_config_set_gateway (ip6_config, &gateway->address);
+			nm_ip6_config_set_gateway (rdisc_config, &gateway->address);
 		} else
-			nm_ip6_config_set_gateway (ip6_config, NULL);
+			nm_ip6_config_set_gateway (rdisc_config, NULL);
 	}
 
 	if (changed & NM_RDISC_CONFIG_ADDRESSES) {
 		/* Rebuild address list from router discovery cache. */
-		nm_ip6_config_reset_addresses (ip6_config);
+		nm_ip6_config_reset_addresses (rdisc_config);
 
 		/* rdisc->addresses contains at most max_addresses entries.
 		 * This is different from what the kernel does, which
@@ -193,13 +197,13 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 			address.source = NM_IP_CONFIG_SOURCE_RDISC;
 			address.n_ifa_flags = ifa_flags;
 
-			nm_ip6_config_add_address (ip6_config, &address);
+			nm_ip6_config_add_address (rdisc_config, &address);
 		}
 	}
 
 	if (changed & NM_RDISC_CONFIG_ROUTES) {
 		/* Rebuild route list from router discovery cache. */
-		nm_ip6_config_reset_routes (ip6_config);
+		nm_ip6_config_reset_routes (rdisc_config);
 
 		for (i = 0; i < rdisc->routes->len; i++) {
 			NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
@@ -217,7 +221,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 				route.source = NM_IP_CONFIG_SOURCE_RDISC;
 				route.metric = global_opt.priority_v6;
 
-				nm_ip6_config_add_route (ip6_config, &route);
+				nm_ip6_config_add_route (rdisc_config, &route);
 			}
 		}
 	}
@@ -236,18 +240,9 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 		nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val);
 	}
 
-	existing = nm_ip6_config_capture (ifindex, FALSE, global_opt.tempaddr);
-	if (last_config)
-		nm_ip6_config_subtract (existing, last_config);
-
-	nm_ip6_config_merge (existing, ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
+	nm_ip6_config_merge (existing, rdisc_config, NM_IP_CONFIG_MERGE_DEFAULT);
 	if (!nm_ip6_config_commit (existing, ifindex, TRUE))
 		nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", global_opt.ifname);
-
-	if (last_config)
-		g_object_unref (last_config);
-	last_config = nm_ip6_config_new (ifindex);
-	nm_ip6_config_replace (last_config, ip6_config, NULL);
 }
 
 static void
@@ -351,6 +346,7 @@ main (int argc, char *argv[])
 	size_t hwaddr_len = 0;
 	gconstpointer tmp;
 	gs_free NMUtilsIPv6IfaceId *iid = NULL;
+	guint sd_id;
 
 	nm_g_type_init ();
 
@@ -474,7 +470,7 @@ main (int argc, char *argv[])
 	if (global_opt.slaac) {
 		nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, TRUE);
 
-		rdisc = nm_lndp_rdisc_new (ifindex, global_opt.ifname, global_opt.uuid, global_opt.addr_gen_mode, NULL);
+		rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, global_opt.ifname, global_opt.uuid, global_opt.addr_gen_mode, NULL);
 		g_assert (rdisc);
 
 		if (iid)
@@ -500,6 +496,8 @@ main (int argc, char *argv[])
 		nm_rdisc_start (rdisc);
 	}
 
+	sd_id = nm_sd_event_attach_default ();
+
 	g_main_loop_run (main_loop);
 
 	g_clear_pointer (&hwaddr, g_byte_array_unref);
@@ -508,6 +506,8 @@ main (int argc, char *argv[])
 		unlink (pidfile);
 
 	nm_log_info (LOGD_CORE, "exiting");
+
+	nm_clear_g_source (&sd_id);
 	exit (0);
 }
 
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 9c2557f9..4b27edf7 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -188,7 +188,7 @@ routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b,
 static gint
 _addresses_sort_cmp_get_prio (in_addr_t addr)
 {
-	if (nmp_utils_ip4_address_is_link_local (addr))
+	if (nm_utils_ip4_address_is_link_local (addr))
 		return 0;
 	return 1;
 }
@@ -1315,7 +1315,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 		g_message ("    nis: %s", nm_utils_inet4_ntop (tmp, NULL));
 	}
 
-	g_message (" nisdmn: %s", str_if_set (nm_ip4_config_get_nis_domain (config), "(none)"));
+	g_message (" nisdmn: %s", nm_ip4_config_get_nis_domain (config) ?: "(none)");
 
 	/* WINS */
 	for (i = 0; i < nm_ip4_config_get_num_wins (config); i++) {
diff --git a/src/nm-logging.c b/src/nm-logging.c
index 5bb3660f..935b34a8 100644
--- a/src/nm-logging.c
+++ b/src/nm-logging.c
@@ -73,6 +73,8 @@ typedef enum {
 	                                                      LOG_FORMAT_FLAG_LOCATION_DEBUG |
 	                                                      LOG_FORMAT_FLAG_LOCATION_ERROR |
 	                                                      LOG_FORMAT_FLAG_ALIGN_LOCATION,
+
+	_LOG_FORMAT_FLAG_DEFAULT                            = _LOG_FORMAT_FLAG_TIMESTAMP,
 } LogFormatFlags;
 
 void (*_nm_logging_clear_platform_logging_cache) (void);
@@ -109,19 +111,20 @@ static struct {
 	char *logging_domains_to_string;
 	const LogLevelDesc level_desc[_LOGL_N];
 
-#define _DOMAIN_DESC_LEN 36
+#define _DOMAIN_DESC_LEN 37
 	/* Would be nice to use C99 flexible array member here,
 	 * but that feature doesn't seem well supported. */
 	const LogDesc domain_desc[_DOMAIN_DESC_LEN];
 } global = {
 	.log_level = LOGL_INFO,
 	.log_backend = LOG_BACKEND_GLIB,
+	.log_format_flags = _LOG_FORMAT_FLAG_DEFAULT,
 	.level_desc = {
 		[LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   _LOG_FORMAT_FLAG_LEVEL_DEBUG },
 		[LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_INFO,    G_LOG_LEVEL_DEBUG,   _LOG_FORMAT_FLAG_LEVEL_DEBUG },
-		[LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_INFO },
-		[LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_WARNING, _LOG_FORMAT_FLAG_LEVEL_INFO },
-		[LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_WARNING, _LOG_FORMAT_FLAG_LEVEL_ERROR },
+		[LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_INFO,    _LOG_FORMAT_FLAG_LEVEL_INFO },
+		[LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_INFO },
+		[LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_ERROR },
 		[_LOGL_OFF]  = { "OFF",   NULL,      0,           0,                   0 },
 		[_LOGL_KEEP] = { "KEEP",  NULL,      0,           0,                   0 },
 	},
@@ -161,6 +164,7 @@ static struct {
 		{ LOGD_DCB,       "DCB" },
 		{ LOGD_DISPATCH,  "DISPATCH" },
 		{ LOGD_AUDIT,     "AUDIT" },
+		{ LOGD_SYSTEMD,   "SYSTEMD" },
 		{ 0, NULL }
 		/* keep _DOMAIN_DESC_LEN in sync */
 	},
@@ -532,7 +536,7 @@ _nm_log_impl (const char *file,
 
 	if (NM_FLAGS_ANY (global.log_format_flags, global.level_desc[level].log_format_level & _LOG_FORMAT_FLAG_TIMESTAMP)) {
 		g_get_current_time (&tv);
-		nm_sprintf_buf (s_buf_timestamp, " [%ld.%06ld]", tv.tv_sec, tv.tv_usec);
+		nm_sprintf_buf (s_buf_timestamp, " [%ld.%04ld]", tv.tv_sec, (tv.tv_usec + 50) / 100);
 	} else
 		s_buf_timestamp[0] = '\0';
 
@@ -722,11 +726,11 @@ nm_log_handler (const gchar *log_domain,
 			boottime = nm_utils_monotonic_timestamp_as_boottime (now, 1);
 
 			sd_journal_send ("PRIORITY=%d", syslog_priority,
-			                 "MESSAGE=%s", str_if_set (message, ""),
+			                 "MESSAGE=%s", message ?: "",
 			                 "SYSLOG_IDENTIFIER=%s", G_LOG_DOMAIN,
 			                 "SYSLOG_PID=%ld", (long) getpid (),
 			                 "SYSLOG_FACILITY=GLIB",
-			                 "GLIB_DOMAIN=%s", str_if_set (log_domain, ""),
+			                 "GLIB_DOMAIN=%s", log_domain ?: "",
 			                 "GLIB_LEVEL=%d", (int) (level & G_LOG_LEVEL_MASK),
 			                 "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000),
 			                 "TIMESTAMP_BOOTTIME=%lld.%06lld", (long long) (boottime / NM_UTILS_NS_PER_SECOND), (long long) ((boottime % NM_UTILS_NS_PER_SECOND) / 1000),
@@ -735,7 +739,7 @@ nm_log_handler (const gchar *log_domain,
 		break;
 #endif
 	default:
-		syslog (syslog_priority, "%s", str_if_set (message, ""));
+		syslog (syslog_priority, "%s", message ?: "");
 		break;
 	}
 }
@@ -751,16 +755,13 @@ nm_logging_syslog_openlog (const char *logging_backend)
 	if (!logging_backend)
 		logging_backend = ""NM_CONFIG_LOGGING_BACKEND_DEFAULT;
 
+	log_format_flags = _LOG_FORMAT_FLAG_DEFAULT;
+
 	if (strcmp (logging_backend, "debug") == 0) {
 		global.log_backend = LOG_BACKEND_SYSLOG;
 		openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER);
-		log_format_flags = _LOG_FORMAT_FLAG_SYSLOG;
 #if SYSTEMD_JOURNAL
 	} else if (strcmp (logging_backend, "syslog") != 0) {
-		if (strcmp (logging_backend, "journal-syslog-style") == 0)
-			log_format_flags = _LOG_FORMAT_FLAG_SYSLOG;
-		else
-			log_format_flags = _LOG_FORMAT_FLAG_TIMESTAMP;
 		global.log_backend = LOG_BACKEND_JOURNAL;
 
 		/* ensure we read a monotonic timestamp. Reading the timestamp the first
@@ -769,7 +770,6 @@ nm_logging_syslog_openlog (const char *logging_backend)
 #endif
 	} else {
 		global.log_backend = LOG_BACKEND_SYSLOG;
-		log_format_flags = _LOG_FORMAT_FLAG_SYSLOG;
 		openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON);
 	}
 
diff --git a/src/nm-logging.h b/src/nm-logging.h
index 97102770..27f89f5d 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -64,6 +64,7 @@ typedef enum  { /*< skip >*/
 	LOGD_DCB        = (1LL << 32), /* Data Center Bridging */
 	LOGD_DISPATCH   = (1LL << 33),
 	LOGD_AUDIT      = (1LL << 34),
+	LOGD_SYSTEMD    = (1LL << 35),
 
 	__LOGD_MAX,
 	LOGD_ALL       = ((__LOGD_MAX - 1LL) << 1) - 1LL,
@@ -118,30 +119,32 @@ typedef enum  { /*< skip >*/
     } G_STMT_END
 
 
-#define _nm_log_ptr(level, domain, self, ...) \
-   nm_log ((level), (domain), "[%p] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), self _NM_UTILS_MACRO_REST(__VA_ARGS__))
+#define _nm_log_ptr(level, domain, self, prefix, ...) \
+   nm_log ((level), (domain), "%s[%p] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), (prefix) ?: "", self _NM_UTILS_MACRO_REST(__VA_ARGS__))
 
 /* log a message for an object (with providing a generic @self pointer) */
-#define nm_log_ptr(level, domain, self, ...) \
+#define nm_log_ptr(level, domain, self, prefix, ...) \
     G_STMT_START { \
         NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") \
         if ((level) <= LOGL_DEBUG) { \
-            _nm_log_ptr ((level), (domain), (self), __VA_ARGS__); \
+            _nm_log_ptr ((level), (domain), (self), (prefix), __VA_ARGS__); \
         } else { \
-            nm_log ((level), (domain), __VA_ARGS__); \
+            const char *__prefix = (prefix); \
+            \
+            nm_log ((level), (domain), "%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), __prefix ?: "", __prefix ? " " : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
         NM_PRAGMA_WARNING_REENABLE \
     } G_STMT_END
 
 
-#define _nm_log_obj(level, domain, self, ...) \
-    _nm_log_ptr ((level), (domain), (self), __VA_ARGS__)
+#define _nm_log_obj(level, domain, self, prefix, ...) \
+    _nm_log_ptr ((level), (domain), (self), prefix, __VA_ARGS__)
 
 /* log a message for an object (with providing a @self pointer to a GObject).
  * Contrary to nm_log_ptr(), @self must be a GObject type (or %NULL).
  * As of now, nm_log_obj() is identical to nm_log_ptr(), but we might change that */
-#define nm_log_obj(level, domain, self, ...) \
-    nm_log_ptr ((level), (domain), (self), __VA_ARGS__)
+#define nm_log_obj(level, domain, self, prefix, ...) \
+    nm_log_ptr ((level), (domain), (self), prefix, __VA_ARGS__)
 
 
 void _nm_log_impl (const char *file,
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 7f414e8f..dc394a49 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -198,20 +198,24 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
 /************************************************************************/
 
-#define _NMLOG_DOMAIN           LOGD_CORE
 #define _NMLOG_PREFIX_NAME      "manager"
-#define _NMLOG(level, ...) \
+#define _NMLOG(level, domain, ...) \
     G_STMT_START { \
-        char __sbuf[32]; \
-        const void *const __self = (self); \
+        const NMLogLevel __level = (level); \
+        const NMLogDomain __domain = (domain); \
         \
-        nm_log ((level), _NMLOG_DOMAIN, \
-                "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                _NMLOG_PREFIX_NAME, \
-                (__self && __self != singleton_instance \
-                    ? (__self ? nm_sprintf_buf (__sbuf, "[%p]", __self) : "[]") \
-                    : "") \
-                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        if (nm_logging_enabled (__level, __domain)) { \
+            const NMManager *const __self = (self); \
+            char __sbuf[32]; \
+            \
+            _nm_log (__level, __domain, 0, \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     (__self && __self != singleton_instance) \
+                         ? nm_sprintf_buf (__sbuf, "[%p]", __self) \
+                         : "" \
+                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
     } G_STMT_END
 
 /************************************************************************/
@@ -222,6 +226,9 @@ static void active_connection_state_changed (NMActiveConnection *active,
 static void active_connection_default_changed (NMActiveConnection *active,
                                                GParamSpec *pspec,
                                                NMManager *self);
+static void active_connection_parent_active (NMActiveConnection *active,
+                                             NMActiveConnection *parent_ac,
+                                             NMManager *self);
 
 /* Returns: whether to notify D-Bus of the removal or not */
 static gboolean
@@ -240,6 +247,7 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 		g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
 		g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
 		g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self);
+		g_signal_handlers_disconnect_by_func (active, active_connection_parent_active, self);
 
 		if (   nm_active_connection_get_assumed (active)
 		    && (connection = nm_active_connection_get_settings_connection (active))
@@ -252,8 +260,8 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 
 		if (   connection
 		    && nm_settings_has_connection (priv->settings, connection)) {
-			nm_log_dbg (LOGD_DEVICE, "Assumed connection disconnected. Deleting generated connection '%s' (%s)",
-			            nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
+			_LOGD (LOGD_DEVICE, "assumed connection disconnected. Deleting generated connection '%s' (%s)",
+			       nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
 			nm_settings_connection_delete (NM_SETTINGS_CONNECTION (connection), NULL, NULL);
 			g_object_unref (connection);
 		}
@@ -603,19 +611,19 @@ _nm_state_to_string (NMState state)
 }
 
 static void
-set_state (NMManager *manager, NMState state)
+set_state (NMManager *self, NMState state)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (priv->state == state)
 		return;
 
 	priv->state = state;
 
-	nm_log_info (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state));
+	_LOGI (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state));
 
-	g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE);
-	g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state);
+	g_object_notify (G_OBJECT (self), NM_MANAGER_STATE);
+	g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state);
 }
 
 static void
@@ -684,14 +692,14 @@ find_best_device_state (NMManager *manager)
 }
 
 static void
-nm_manager_update_metered (NMManager *manager)
+nm_manager_update_metered (NMManager *self)
 {
 	NMManagerPrivate *priv;
 	NMDevice *device;
 	NMMetered value = NM_METERED_UNKNOWN;
 
-	g_return_if_fail (NM_IS_MANAGER (manager));
-	priv = NM_MANAGER_GET_PRIVATE (manager);
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
 	if (priv->primary_connection) {
 		device =  nm_active_connection_get_device (priv->primary_connection);
@@ -701,9 +709,8 @@ nm_manager_update_metered (NMManager *manager)
 
 	if (value != priv->metered) {
 		priv->metered = value;
-		nm_log_dbg (LOGD_CORE, "New manager metered value: %d",
-		            (int) priv->metered);
-		g_object_notify (G_OBJECT (manager), NM_MANAGER_METERED);
+		_LOGD (LOGD_CORE, "new metered value: %d", (int) priv->metered);
+		g_object_notify (G_OBJECT (self), NM_MANAGER_METERED);
 	}
 }
 
@@ -777,7 +784,7 @@ check_if_startup_complete (NMManager *self)
 		return;
 
 	if (!nm_settings_get_startup_complete (priv->settings)) {
-		nm_log_dbg (LOGD_CORE, "check_if_startup_complete returns FALSE because of NMSettings");
+		_LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of NMSettings");
 		return;
 	}
 
@@ -785,13 +792,13 @@ check_if_startup_complete (NMManager *self)
 		NMDevice *dev = iter->data;
 
 		if (nm_device_has_pending_action (dev)) {
-			nm_log_dbg (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s",
-			            nm_device_get_iface (dev));
+			_LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s",
+			       nm_device_get_iface (dev));
 			return;
 		}
 	}
 
-	nm_log_info (LOGD_CORE, "startup complete");
+	_LOGI (LOGD_CORE, "startup complete");
 
 	priv->startup = FALSE;
 	g_object_notify (G_OBJECT (self), "startup");
@@ -824,15 +831,15 @@ settings_startup_complete_changed (NMSettings *settings,
 }
 
 static void
-remove_device (NMManager *manager,
+remove_device (NMManager *self,
                NMDevice *device,
                gboolean quitting,
                gboolean allow_unmanage)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): removing device (allow_unmanage %d, managed %d)",
-	            nm_device_get_iface (device), allow_unmanage, nm_device_get_managed (device, FALSE));
+	_LOGD (LOGD_DEVICE, "(%s): removing device (allow_unmanage %d, managed %d)",
+	       nm_device_get_iface (device), allow_unmanage, nm_device_get_managed (device, FALSE));
 
 	if (allow_unmanage && nm_device_get_managed (device, FALSE)) {
 		NMActRequest *req = nm_device_get_act_request (device);
@@ -860,22 +867,22 @@ remove_device (NMManager *manager,
 		}
 	}
 
-	g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
+	g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
 
 	nm_settings_device_removed (priv->settings, device, quitting);
 	priv->devices = g_slist_remove (priv->devices, device);
 
 	if (nm_device_is_real (device)) {
-		g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device);
-		g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES);
+		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
+		g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES);
 		nm_device_removed (device);
 	}
-	g_signal_emit (manager, signals[INTERNAL_DEVICE_REMOVED], 0, device);
-	g_object_notify (G_OBJECT (manager), NM_MANAGER_ALL_DEVICES);
+	g_signal_emit (self, signals[INTERNAL_DEVICE_REMOVED], 0, device);
+	g_object_notify (G_OBJECT (self), NM_MANAGER_ALL_DEVICES);
 
 	nm_exported_object_clear_and_unexport (&device);
 
-	check_if_startup_complete (manager);
+	check_if_startup_complete (self);
 }
 
 static void
@@ -1039,8 +1046,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 
 	iface = nm_manager_get_connection_iface (self, connection, &parent, &error);
 	if (!iface) {
-		nm_log_warn (LOGD_DEVICE, "(%s) can't get a name of a virtual device: %s",
-		             nm_connection_get_id (connection), error->message);
+		_LOGD (LOGD_DEVICE, "(%s) can't get a name of a virtual device: %s",
+		       nm_connection_get_id (connection), error->message);
 		g_error_free (error);
 		return NULL;
 	}
@@ -1051,8 +1058,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 
 		if (nm_device_check_connection_compatible (candidate, connection)) {
 			if (nm_device_is_real (candidate)) {
-				nm_log_dbg (LOGD_DEVICE, "(%s) already created virtual interface name %s",
-				            nm_connection_get_id (connection), iface);
+				_LOGD (LOGD_DEVICE, "(%s) already created virtual interface name %s",
+				       nm_connection_get_id (connection), iface);
 				return NULL;
 			}
 
@@ -1066,23 +1073,27 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 
 		factory = nm_device_factory_manager_find_factory_for_connection (connection);
 		if (!factory) {
-			nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable",
-			            nm_connection_get_id (connection), iface,
-			            nm_connection_get_connection_type (connection));
+			_LOGE (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable",
+			       nm_connection_get_id (connection), iface,
+			       nm_connection_get_connection_type (connection));
 			return NULL;
 		}
 
 		device = nm_device_factory_create_device (factory, iface, NULL, connection, NULL, &error);
 		if (!device) {
-			nm_log_warn (LOGD_DEVICE, "(%s) factory can't create the device: %s",
-			             nm_connection_get_id (connection), error->message);
+			_LOGW (LOGD_DEVICE, "(%s) factory can't create the device: %s",
+			       nm_connection_get_id (connection), error->message);
 			g_error_free (error);
 			return NULL;
 		}
 
+		_LOGD (LOGD_DEVICE, "(%s) create virtual device %s",
+		       nm_connection_get_id (connection),
+		       nm_device_get_iface (device));
+
 		if (!add_device (self, device, &error)) {
-			nm_log_warn (LOGD_DEVICE, "(%s) can't register the device with manager: %s",
-			             nm_connection_get_id (connection), error->message);
+			_LOGW (LOGD_DEVICE, "(%s) can't register the device with manager: %s",
+			       nm_connection_get_id (connection), error->message);
 			g_error_free (error);
 			g_object_unref (device);
 			return NULL;
@@ -1110,8 +1121,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 
 		/* Create any backing resources the device needs */
 		if (!nm_device_create_and_realize (device, connection, parent, &error)) {
-			nm_log_warn (LOGD_DEVICE, "(%s) couldn't create the device: %s",
-			             nm_connection_get_id (connection), error->message);
+			_LOGW (LOGD_DEVICE, "(%s) couldn't create the device: %s",
+			       nm_connection_get_id (connection), error->message);
 			g_error_free (error);
 			remove_device (self, device, FALSE, TRUE);
 			return NULL;
@@ -1133,11 +1144,19 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *candidate = iter->data;
+		gs_free_error GError *error = NULL;
+		gs_free char *ifname = NULL;
 		NMDevice *parent;
 
 		parent = find_parent_device_for_connection (self, candidate, NULL);
-		if (parent == device)
-			connection_changed (priv->settings, candidate, self);
+		if (parent == device) {
+			/* Only try to activate devices that don't already exist */
+			ifname = nm_manager_get_connection_iface (self, candidate, &parent, &error);
+			if (ifname) {
+				if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname))
+					connection_changed (priv->settings, candidate, self);
+			}
+		}
 	}
 
 	g_slist_free (connections);
@@ -1314,9 +1333,9 @@ manager_update_radio_enabled (NMManager *self,
 		NMDevice *device = NM_DEVICE (iter->data);
 
 		if (nm_device_get_rfkill_type (device) == rstate->rtype) {
-			nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s",
-			            nm_device_get_iface (device),
-			            enabled ? "enabled" : "disabled");
+			_LOGD (LOGD_RFKILL, "(%s): setting radio %s",
+			       nm_device_get_iface (device),
+			       enabled ? "enabled" : "disabled");
 			nm_device_set_enabled (device, enabled);
 		}
 	}
@@ -1361,16 +1380,16 @@ manager_rfkill_update_one_type (NMManager *self,
 
 	/* Print out all states affecting device enablement */
 	if (rstate->desc) {
-		nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d",
-		            rstate->desc, rstate->hw_enabled, rstate->sw_enabled);
+		_LOGD (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d",
+		       rstate->desc, rstate->hw_enabled, rstate->sw_enabled);
 	}
 
 	/* Log new killswitch state */
 	new_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
 	if (old_rfkilled != new_rfkilled) {
-		nm_log_info (LOGD_RFKILL, "%s now %s by radio killswitch",
-		             rstate->desc,
-		             new_rfkilled ? "enabled" : "disabled");
+		_LOGI (LOGD_RFKILL, "%s now %s by radio killswitch",
+		       rstate->desc,
+		       new_rfkilled ? "enabled" : "disabled");
 	}
 
 	/* Send out property changed signal for HW enabled */
@@ -1435,13 +1454,13 @@ device_auth_done_cb (NMAuthChain *chain,
 
 	if (auth_error) {
 		/* translate the auth error into a manager permission denied error */
-		nm_log_dbg (LOGD_CORE, "%s request failed: %s", permission, auth_error->message);
+		_LOGD (LOGD_CORE, "%s request failed: %s", permission, auth_error->message);
 		error = g_error_new (NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                     "%s request failed: %s",
 		                     permission, auth_error->message);
 	} else if (result != NM_AUTH_CALL_RESULT_YES) {
-		nm_log_dbg (LOGD_CORE, "%s request failed: not authorized", permission);
+		_LOGD (LOGD_CORE, "%s request failed: not authorized", permission);
 		error = g_error_new (NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                     "%s request failed: not authorized",
@@ -1539,10 +1558,10 @@ match_connection_filter (NMConnection *connection, gpointer user_data)
  *   the device does not support assuming existing connections.
  */
 static NMSettingsConnection *
-get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_generated)
+get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_generated)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	gs_free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gs_free_slist GSList *connections = nm_manager_get_activatable_connections (self);
 	NMConnection *connection = NULL;
 	NMSettingsConnection *matched;
 	NMSettingsConnection *added = NULL;
@@ -1559,15 +1578,15 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen
 		int master_ifindex = nm_platform_link_get_master (NM_PLATFORM_GET, ifindex);
 
 		if (master_ifindex) {
-			master = nm_manager_get_device_by_ifindex (manager, master_ifindex);
+			master = nm_manager_get_device_by_ifindex (self, master_ifindex);
 			if (!master) {
-				nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s/%d)",
-				            nm_device_get_iface (device), nm_platform_link_get_name (NM_PLATFORM_GET, master_ifindex), master_ifindex);
+				_LOGD (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s/%d)",
+				       nm_device_get_iface (device), nm_platform_link_get_name (NM_PLATFORM_GET, master_ifindex), master_ifindex);
 				return NULL;
 			}
 			if (!nm_device_get_act_request (master)) {
-				nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before master %s activates",
-				            nm_device_get_iface (device), nm_device_get_iface (master));
+				_LOGD (LOGD_DEVICE, "(%s): cannot generate connection for slave before master %s activates",
+				       nm_device_get_iface (device), nm_device_get_iface (master));
 				return NULL;
 			}
 		}
@@ -1601,16 +1620,16 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen
 	                                                             match_connection_filter,
 	                                                             device));
 	if (matched) {
-		nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
-		             nm_device_get_iface (device),
-		             nm_settings_connection_get_id (matched));
+		_LOGI (LOGD_DEVICE, "(%s): found matching connection '%s'",
+		       nm_device_get_iface (device),
+		       nm_settings_connection_get_id (matched));
 		g_object_unref (connection);
 		return matched;
 	}
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'",
-	            nm_device_get_iface (device),
-	            nm_connection_get_id (connection));
+	_LOGD (LOGD_DEVICE, "(%s): generated connection '%s'",
+	       nm_device_get_iface (device),
+	       nm_connection_get_id (connection));
 
 	added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
 	if (added) {
@@ -1621,10 +1640,10 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen
 		if (out_generated)
 			*out_generated = TRUE;
 	} else {
-		nm_log_warn (LOGD_SETTINGS, "(%s) Couldn't save generated connection '%s': %s",
-		             nm_device_get_iface (device),
-		             nm_connection_get_id (connection),
-		             (error && error->message) ? error->message : "(unknown)");
+		_LOGW (LOGD_SETTINGS, "(%s) Couldn't save generated connection '%s': %s",
+		       nm_device_get_iface (device),
+		       nm_connection_get_id (connection),
+		       error->message);
 		g_clear_error (&error);
 	}
 	g_object_unref (connection);
@@ -1639,8 +1658,8 @@ assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *conn
 	NMAuthSubject *subject;
 	GError *error = NULL;
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection",
-	            nm_device_get_iface (device));
+	_LOGD (LOGD_DEVICE, "(%s): will attempt to assume connection",
+	       nm_device_get_iface (device));
 
 	/* Move device to DISCONNECTED to activate the connection */
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) {
@@ -1655,10 +1674,9 @@ assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *conn
 	g_object_unref (subject);
 
 	if (!active) {
-		nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s",
-		             nm_connection_get_path (NM_CONNECTION (connection)),
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		_LOGW (LOGD_DEVICE, "assumed connection %s failed to activate: %s",
+		       nm_connection_get_path (NM_CONNECTION (connection)),
+		       error->message);
 		g_error_free (error);
 		return FALSE;
 	}
@@ -1699,8 +1717,8 @@ recheck_assume_connection (NMManager *self, NMDevice *device)
 
 	connection = get_existing_connection (self, device, &generated);
 	if (!connection) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): can't assume; no connection",
-		            nm_device_get_iface (device));
+		_LOGD (LOGD_DEVICE, "(%s): can't assume; no connection",
+		       nm_device_get_iface (device));
 		return FALSE;
 	}
 
@@ -1720,8 +1738,8 @@ recheck_assume_connection (NMManager *self, NMDevice *device)
 		}
 
 		if (generated) {
-			nm_log_dbg (LOGD_DEVICE, "(%s): connection assumption failed. Deleting generated connection",
-			            nm_device_get_iface (device));
+			_LOGD (LOGD_DEVICE, "(%s): connection assumption failed. Deleting generated connection",
+			       nm_device_get_iface (device));
 
 			nm_settings_connection_delete (connection, NULL, NULL);
 		}
@@ -1908,7 +1926,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                               manager_sleeping (self));
 
 	dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device));
-	nm_log_info (LOGD_DEVICE, "(%s): new %s device (%s)", iface, type_desc, dbus_path);
+	_LOGI (LOGD_DEVICE, "(%s): new %s device (%s)", iface, type_desc, dbus_path);
 
 	nm_settings_device_added (priv->settings, device);
 	g_signal_emit (self, signals[INTERNAL_DEVICE_ADDED], 0, device);
@@ -1945,8 +1963,8 @@ factory_device_added_cb (NMDeviceFactory *factory,
 		add_device (self, device, NULL);
 		_device_realize_finish (self, device, NULL);
 	} else {
-		nm_log_warn (LOGD_DEVICE, "(%s): failed to realize device: %s",
-		             nm_device_get_iface (device), error->message);
+		_LOGW (LOGD_DEVICE, "(%s): failed to realize device: %s",
+		       nm_device_get_iface (device), error->message);
 		g_error_free (error);
 	}
 }
@@ -2019,8 +2037,8 @@ platform_link_added (NMManager *self,
 			return;
 		}
 
-		nm_log_dbg (LOGD_DEVICE, "(%s): failed to realize from plink: '%s'",
-		            plink->name, error->message);
+		_LOGD (LOGD_DEVICE, "(%s): failed to realize from plink: '%s'",
+		       plink->name, error->message);
 		g_clear_error (&error);
 
 		/* Try next unrealized device */
@@ -2034,8 +2052,8 @@ platform_link_added (NMManager *self,
 		device = nm_device_factory_create_device (factory, plink->name, plink, NULL, &ignore, &error);
 		if (!device) {
 			if (!ignore) {
-				nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s",
-				             plink->name, error->message);
+				_LOGW (LOGD_HW, "%s: factory failed to create device: %s",
+				       plink->name, error->message);
 				g_clear_error (&error);
 			}
 			return;
@@ -2049,8 +2067,8 @@ platform_link_added (NMManager *self,
 		case NM_LINK_TYPE_OLPC_MESH:
 		case NM_LINK_TYPE_TEAM:
 		case NM_LINK_TYPE_WIFI:
-			nm_log_info (LOGD_HW, "(%s): '%s' plugin not available; creating generic device",
-			             plink->name, nm_link_type_to_string (plink->type));
+			_LOGI (LOGD_HW, "(%s): '%s' plugin not available; creating generic device",
+			       plink->name, nm_link_type_to_string (plink->type));
 			nm_plugin_missing = TRUE;
 			/* fall through */
 		default:
@@ -2066,8 +2084,8 @@ platform_link_added (NMManager *self,
 			add_device (self, device, NULL);
 			_device_realize_finish (self, device, plink);
 		} else {
-			nm_log_warn (LOGD_DEVICE, "%s: failed to realize device: %s",
-			             plink->name, error->message);
+			_LOGW (LOGD_DEVICE, "%s: failed to realize device: %s",
+			       plink->name, error->message);
 			g_clear_error (&error);
 		}
 		g_object_unref (device);
@@ -2105,9 +2123,9 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
 			if (nm_device_is_software (device)) {
 				/* Our software devices stick around until their connection is removed */
 				if (!nm_device_unrealize (device, FALSE, &error)) {
-					nm_log_warn (LOGD_DEVICE, "(%s): failed to unrealize: %s",
-					             nm_device_get_iface (device),
-					             error->message);
+					_LOGW (LOGD_DEVICE, "(%s): failed to unrealize: %s",
+					       nm_device_get_iface (device),
+					       error->message);
 					g_clear_error (&error);
 					remove_device (self, device, FALSE, TRUE);
 				}
@@ -2654,7 +2672,7 @@ out:
 }
 
 static gboolean
-autoconnect_slaves (NMManager *manager,
+autoconnect_slaves (NMManager *self,
                     NMSettingsConnection *master_connection,
                     NMDevice *master_device,
                     NMAuthSubject *subject)
@@ -2665,28 +2683,28 @@ autoconnect_slaves (NMManager *manager,
 	if (should_connect_slaves (NM_CONNECTION (master_connection), master_device)) {
 		GSList *slaves, *iter;
 
-		iter = slaves = find_slaves (manager, master_connection, master_device);
+		iter = slaves = find_slaves (self, master_connection, master_device);
 		ret = slaves != NULL;
 
 		while (iter) {
 			NMSettingsConnection *slave_connection = iter->data;
 
 			iter = iter->next;
-			nm_log_dbg (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
-			            nm_settings_connection_get_id (slave_connection),
-			            nm_settings_connection_get_uuid (slave_connection),
-			            nm_settings_connection_get_id (master_connection),
-			            nm_settings_connection_get_uuid (master_connection));
+			_LOGD (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
+			       nm_settings_connection_get_id (slave_connection),
+			       nm_settings_connection_get_uuid (slave_connection),
+			       nm_settings_connection_get_id (master_connection),
+			       nm_settings_connection_get_uuid (master_connection));
 
 			/* Schedule slave activation */
-			nm_manager_activate_connection (manager,
+			nm_manager_activate_connection (self,
 			                                slave_connection,
 			                                NULL,
-			                                nm_manager_get_best_device_for_connection (manager, NM_CONNECTION (slave_connection), FALSE),
+			                                nm_manager_get_best_device_for_connection (self, NM_CONNECTION (slave_connection), FALSE),
 			                                subject,
 			                                &local_err);
 			if (local_err) {
-				nm_log_warn (LOGD_CORE, "Slave connection activation failed: %s", local_err->message);
+				_LOGW (LOGD_CORE, "Slave connection activation failed: %s", local_err->message);
 				g_error_free (local_err);
 			}
 		}
@@ -2713,6 +2731,71 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er
 	return success;
 }
 
+/* Traverse the device to disconnected state. This means that the device is ready
+ * for connection and will proceed activating if there's an activation request
+ * enqueued.
+ */
+static void
+unmanaged_to_disconnected (NMDevice *device)
+{
+	/* when creating the software device, it can happen that the device is
+	 * still unmanaged by NM_UNMANAGED_PLATFORM_INIT because we didn't yet
+	 * get the udev event. At this point, we can no longer delay the activation
+	 * and force the device to be managed. */
+	nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
+
+	nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_USER_EXPLICIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
+
+	g_return_if_fail (nm_device_get_managed (device, FALSE));
+
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) {
+		nm_device_state_changed (device,
+					 NM_DEVICE_STATE_UNAVAILABLE,
+					 NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	}
+
+	if (   nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)
+	    && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
+		nm_device_state_changed (device,
+					 NM_DEVICE_STATE_DISCONNECTED,
+					 NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	}
+}
+
+/* The parent connection is ready; we can proceed realizing the device and
+ * progressing the device to disconencted state.
+ */
+static void
+active_connection_parent_active (NMActiveConnection *active,
+                                 NMActiveConnection *parent_ac,
+                                 NMManager *self)
+{
+	NMDevice *device = nm_active_connection_get_device (active);
+	GError *error = NULL;
+
+	g_signal_handlers_disconnect_by_func (active,
+	                                      (GCallback) active_connection_parent_active,
+	                                      self);
+
+	if (parent_ac) {
+		NMSettingsConnection *connection = nm_active_connection_get_settings_connection (active);
+		NMDevice *parent = nm_active_connection_get_device (parent_ac);
+
+		if (nm_device_create_and_realize (device, (NMConnection *) connection, parent, &error)) {
+			/* We can now proceed to disconnected state so that activation proceeds. */
+			unmanaged_to_disconnected (device);
+		} else {
+			nm_log_warn (LOGD_CORE, "Could not realize device '%s': %s",
+			             nm_device_get_iface (device), error->message);
+			nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+		}
+	} else {
+		nm_log_warn (LOGD_CORE, "The parent connection device '%s' depended on disappeared.",
+		             nm_device_get_iface (device));
+		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+	}
+}
+
 static gboolean
 _internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error)
 {
@@ -2772,9 +2855,35 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		NMDevice *parent;
 
 		parent = find_parent_device_for_connection (self, (NMConnection *) connection, NULL);
-		if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
-			g_prefix_error (error, "%s failed to create resources: ", nm_device_get_iface (device));
-			return FALSE;
+
+		if (parent && !nm_device_is_real (parent)) {
+			NMSettingsConnection *parent_con;
+			NMActiveConnection *parent_ac;
+
+			parent_con = nm_device_get_best_connection (parent, NULL, error);
+			if (!parent_con) {
+				g_prefix_error (error, "%s failed to create parent: ", nm_device_get_iface (device));
+				return FALSE;
+			}
+
+			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, parent, subject, error);
+			if (!parent_ac) {
+				g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device));
+				return FALSE;
+			}
+
+			/* We can't realize now; defer until the parent device is ready. */
+			g_signal_connect (active,
+			                  NM_ACTIVE_CONNECTION_PARENT_ACTIVE,
+			                  (GCallback) active_connection_parent_active,
+			                  self);
+			nm_active_connection_set_parent (active, parent_ac);
+		} else {
+			/* We can realize now; no need to wait for a parent device. */
+			if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
+				g_prefix_error (error, "%s failed to create resources: ", nm_device_get_iface (device));
+				return FALSE;
+			}
 		}
 	}
 
@@ -2789,14 +2898,14 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	 */
 	if (master_connection || master_device) {
 		if (master_connection) {
-			nm_log_dbg (LOGD_CORE, "Activation of '%s' requires master connection '%s'",
-			            nm_settings_connection_get_id (connection),
-			            nm_settings_connection_get_id (master_connection));
+			_LOGD (LOGD_CORE, "Activation of '%s' requires master connection '%s'",
+			       nm_settings_connection_get_id (connection),
+			       nm_settings_connection_get_id (master_connection));
 		}
 		if (master_device) {
-			nm_log_dbg (LOGD_CORE, "Activation of '%s' requires master device '%s'",
-			            nm_settings_connection_get_id (connection),
-			            nm_device_get_ip_iface (master_device));
+			_LOGD (LOGD_CORE, "Activation of '%s' requires master device '%s'",
+			       nm_settings_connection_get_id (connection),
+			       nm_device_get_ip_iface (master_device));
 		}
 
 		/* Ensure eg bond slave and the candidate master is a bond master */
@@ -2822,10 +2931,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		}
 
 		nm_active_connection_set_master (active, master_ac);
-		nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p %s",
-		            nm_settings_connection_get_id (connection),
-		            master_ac,
-		            str_if_set (nm_exported_object_get_path (NM_EXPORTED_OBJECT  (master_ac)), ""));
+		_LOGD (LOGD_CORE, "Activation of '%s' depends on active connection %p %s",
+		       nm_settings_connection_get_id (connection),
+		       master_ac,
+		       nm_exported_object_get_path (NM_EXPORTED_OBJECT  (master_ac)) ?: "");
 	}
 
 	/* Check slaves for master connection and possibly activate them */
@@ -2836,28 +2945,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	if (existing)
 		nm_device_steal_connection (existing, connection);
 
-	/* when creating the software device, it can happen that the device is
-	 * still unmanaged by NM_UNMANAGED_PLATFORM_INIT because we didn't yet
-	 * get the udev event. At this point, we can no longer delay the activation
-	 * and force the device to be managed. */
-	nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
-
-	nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_USER_EXPLICIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
-
-	g_return_val_if_fail (nm_device_get_managed (device, FALSE), FALSE);
-
-	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_UNAVAILABLE,
-		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
-	}
-
-	if (   nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)
-	    && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
-	}
+	/* If the device is there, we can ready it for the activation. */
+	if (nm_device_is_real (device))
+		unmanaged_to_disconnected (device);
 
 	/* Export the new ActiveConnection to clients and start it on the device */
 	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
@@ -2998,9 +3088,9 @@ _internal_activation_failed (NMManager *self,
                              NMActiveConnection *active,
                              const char *error_desc)
 {
-	nm_log_dbg (LOGD_CORE, "Failed to activate '%s': %s",
-	            nm_active_connection_get_settings_connection_id (active),
-	            error_desc);
+	_LOGD (LOGD_CORE, "Failed to activate '%s': %s",
+	       nm_active_connection_get_settings_connection_id (active),
+	       error_desc);
 
 	if (nm_active_connection_get_state (active) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
 		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
@@ -3315,11 +3405,15 @@ impl_manager_activate_connection (NMManager *self,
 	 * regardless of whether that connection is autoconnect-enabled or not
 	 * (since this is an explicit request, not an auto-activation request).
 	 */
-	if (!connection_path) {
-		GPtrArray *available;
-		guint64 best_timestamp = 0;
-		guint i;
-
+	if (connection_path) {
+		connection = nm_settings_get_connection_by_path (priv->settings, connection_path);
+		if (!connection) {
+			error = g_error_new_literal (NM_MANAGER_ERROR,
+						     NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+						     "Connection could not be found.");
+			goto error;
+		}
+	} else {
 		/* If no connection is given, find a suitable connection for the given device path */
 		if (!device_path) {
 			error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
@@ -3333,36 +3427,9 @@ impl_manager_activate_connection (NMManager *self,
 			goto error;
 		}
 
-		available = nm_device_get_available_connections (device, specific_object_path);
-		for (i = 0; available && i < available->len; i++) {
-			NMSettingsConnection *candidate = g_ptr_array_index (available, i);
-			guint64 candidate_timestamp = 0;
-
-			nm_settings_connection_get_timestamp (candidate, &candidate_timestamp);
-			if (!connection_path || (candidate_timestamp > best_timestamp)) {
-				connection_path = nm_connection_get_path (NM_CONNECTION (candidate));
-				best_timestamp = candidate_timestamp;
-			}
-		}
-
-		if (available)
-			g_ptr_array_free (available, TRUE);
-
-		if (!connection_path) {
-			error = g_error_new_literal (NM_MANAGER_ERROR,
-			                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-			                             "The device has no connections available.");
+		connection = nm_device_get_best_connection (device, specific_object_path, &error);
+		if (!connection)
 			goto error;
-		}
-	}
-
-	g_assert (connection_path);
-	connection = nm_settings_get_connection_by_path (priv->settings, connection_path);
-	if (!connection) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
-		                             "Connection could not be found.");
-		goto error;
 	}
 
 	subject = validate_activation_request (self,
@@ -3536,7 +3603,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	 */
 	connection = nm_simple_connection_new ();
 	if (settings && g_variant_n_children (settings))
-		nm_connection_replace_settings (connection, settings, NULL);
+		_nm_connection_replace_settings (connection, settings, NM_SETTING_PARSE_FLAGS_STRICT, NULL);
 
 	subject = validate_activation_request (self,
 	                                       context,
@@ -3559,7 +3626,8 @@ impl_manager_add_and_activate_connection (NMManager *self,
 			goto error;
 		}
 
-		nm_utils_complete_generic (connection,
+		nm_utils_complete_generic (NM_PLATFORM_GET,
+		                           connection,
 		                           NM_SETTING_VPN_SETTING_NAME,
 		                           all_connections,
 		                           NULL,
@@ -3668,10 +3736,9 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 
 	path = nm_auth_chain_get_data (chain, "path");
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL);
-	active = active_connection_get_by_path (self, path);
 
 	if (auth_error) {
-		nm_log_dbg (LOGD_CORE, "Disconnect request failed: %s", auth_error->message);
+		_LOGD (LOGD_CORE, "Disconnect request failed: %s", auth_error->message);
 		error = g_error_new (NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                     "Deactivate request failed: %s",
@@ -3686,9 +3753,10 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 		                                       path,
 		                                       NM_DEVICE_STATE_REASON_USER_REQUESTED,
 		                                       &error))
-			g_assert (error);
+			nm_assert (error);
 	}
 
+	active = active_connection_get_by_path (self, path);
 	if (active) {
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE,
 		                            nm_active_connection_get_settings_connection (active),
@@ -3791,7 +3859,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 	waking_from_suspend = sleeping_changed && !priv->sleeping;
 
 	if (manager_sleeping (self)) {
-		nm_log_info (LOGD_SUSPEND, "%s...", suspending ? "sleeping" : "disabling");
+		_LOGI (LOGD_SUSPEND, "%s...", suspending ? "sleeping" : "disabling");
 
 		/* FIXME: are there still hardware devices that need to be disabled around
 		 * suspend/resume?
@@ -3809,7 +3877,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 			nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_SLEEPING, TRUE, NM_DEVICE_STATE_REASON_SLEEPING);
 		}
 	} else {
-		nm_log_info (LOGD_SUSPEND, "%s...", waking_from_suspend ? "waking up" : "re-enabling");
+		_LOGI (LOGD_SUSPEND, "%s...", waking_from_suspend ? "waking up" : "re-enabling");
 
 		if (waking_from_suspend) {
 			/* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this
@@ -3846,9 +3914,9 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 				gboolean enabled = radio_enabled_for_rstate (rstate, TRUE);
 
 				if (rstate->desc) {
-					nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)",
-					            enabled ? "enabling" : "disabling",
-					            rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->user_enabled);
+					_LOGD (LOGD_RFKILL, "%s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)",
+					       enabled ? "enabling" : "disabling",
+					       rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->user_enabled);
 				}
 
 				if (nm_device_get_rfkill_type (device) == rstate->rtype)
@@ -3872,10 +3940,10 @@ _internal_sleep (NMManager *self, gboolean do_sleep)
 	if (priv->sleeping == do_sleep)
 		return;
 
-	nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
-	             do_sleep ? "sleep" : "wake",
-	             priv->sleeping ? "yes" : "no",
-	             priv->net_enabled ? "yes" : "no");
+	_LOGI (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
+	       do_sleep ? "sleep" : "wake",
+	       priv->sleeping ? "yes" : "no",
+	       priv->net_enabled ? "yes" : "no");
 
 	priv->sleeping = do_sleep;
 
@@ -3901,7 +3969,7 @@ sleep_auth_done_cb (NMAuthChain *chain,
 
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SLEEP_WAKE);
 	if (error) {
-		nm_log_dbg (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message);
+		_LOGD (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message);
 		ret_error = g_error_new (NM_MANAGER_ERROR,
 		                         NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                         "Sleep/wake request failed: %s",
@@ -4006,17 +4074,16 @@ _internal_enable (NMManager *self, gboolean enable)
 		                                G_TYPE_BOOLEAN, (gpointer) &enable,
 		                                &err)) {
 			/* Not a hard error */
-			nm_log_warn (LOGD_SUSPEND, "writing to state file %s failed: (%d) %s.",
-			             priv->state_file,
-			             err ? err->code : -1,
-			             (err && err->message) ? err->message : "unknown");
+			_LOGW (LOGD_SUSPEND, "writing to state file %s failed: %s",
+			       priv->state_file,
+			       err->message);
 		}
 	}
 
-	nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
-	             enable ? "enable" : "disable",
-	             priv->sleeping ? "yes" : "no",
-	             priv->net_enabled ? "yes" : "no");
+	_LOGI (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
+	       enable ? "enable" : "disable",
+	       priv->sleeping ? "yes" : "no",
+	       priv->net_enabled ? "yes" : "no");
 
 	priv->net_enabled = enable;
 
@@ -4046,7 +4113,7 @@ enable_net_done_cb (NMAuthChain *chain,
 
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK);
 	if (error) {
-		nm_log_dbg (LOGD_CORE, "Enable request failed: %s", error->message);
+		_LOGD (LOGD_CORE, "Enable request failed: %s", error->message);
 		ret_error = g_error_new (NM_MANAGER_ERROR,
 		                         NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                         "Enable request failed: %s",
@@ -4143,7 +4210,7 @@ get_permissions_done_cb (NMAuthChain *chain,
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 	if (error) {
-		nm_log_dbg (LOGD_CORE, "Permissions request failed: %s", error->message);
+		_LOGD (LOGD_CORE, "Permissions request failed: %s", error->message);
 		ret_error = g_error_new (NM_MANAGER_ERROR,
 		                         NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                         "Permissions request failed: %s",
@@ -4236,8 +4303,8 @@ impl_manager_set_logging (NMManager *self,
 	}
 
 	if (nm_logging_setup (level, domains, NULL, &error)) {
-		nm_log_info (LOGD_CORE, "logging: level '%s' domains '%s'",
-		             nm_logging_level_to_string (), nm_logging_domains_to_string ());
+		_LOGI (LOGD_CORE, "logging: level '%s' domains '%s'",
+		       nm_logging_level_to_string (), nm_logging_domains_to_string ());
 	}
 
 done:
@@ -4292,7 +4359,7 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL);
 
 	if (auth_error) {
-		nm_log_dbg (LOGD_CORE, "CheckConnectivity request failed: %s", auth_error->message);
+		_LOGD (LOGD_CORE, "CheckConnectivity request failed: %s", auth_error->message);
 		error = g_error_new (NM_MANAGER_ERROR,
 		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
 		                     "Connectivity check request failed: %s",
@@ -4368,18 +4435,18 @@ nm_manager_start (NMManager *self, GError **error)
 		update_rstate_from_rfkill (priv->rfkill_mgr, rstate);
 
 		if (rstate->desc) {
-			nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
-			             rstate->desc,
-			             (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
-			             rstate->user_enabled ? "enabled" : "disabled");
+			_LOGI (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
+			       rstate->desc,
+			       (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
+			       rstate->user_enabled ? "enabled" : "disabled");
 		}
 		enabled = radio_enabled_for_rstate (rstate, TRUE);
 		manager_update_radio_enabled (self, rstate, enabled);
 	}
 
 	/* Log overall networking status - enabled/disabled */
-	nm_log_info (LOGD_CORE, "Networking is %s by state file",
-	             priv->net_enabled ? "enabled" : "disabled");
+	_LOGI (LOGD_CORE, "Networking is %s by state file",
+	       priv->net_enabled ? "enabled" : "disabled");
 
 	system_unmanaged_devices_changed_cb (priv->settings, NULL, self);
 	system_hostname_changed_cb (priv->settings, NULL, self);
@@ -4396,7 +4463,7 @@ nm_manager_start (NMManager *self, GError **error)
 	/* Connections added before the manager is started do not emit
 	 * connection-added signals thus devices have to be created manually.
 	 */
-	nm_log_dbg (LOGD_CORE, "creating virtual devices...");
+	_LOGD (LOGD_CORE, "creating virtual devices...");
 	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = iter->next)
 		connection_changed (priv->settings, NM_CONNECTION (iter->data), self);
@@ -4437,8 +4504,8 @@ handle_firmware_changed (gpointer user_data)
 
 		if (   nm_device_get_firmware_missing (candidate)
 		    && (state == NM_DEVICE_STATE_UNAVAILABLE)) {
-			nm_log_info (LOGD_CORE, "(%s): firmware may now be available",
-			             nm_device_get_iface (candidate));
+			_LOGI (LOGD_CORE, "(%s): firmware may now be available",
+			       nm_device_get_iface (candidate));
 
 			/* Re-set unavailable state to try bringing the device up again */
 			nm_device_state_changed (candidate,
@@ -4457,8 +4524,8 @@ connectivity_changed (NMConnectivity *connectivity,
 {
 	NMManager *self = NM_MANAGER (user_data);
 
-	nm_log_dbg (LOGD_CORE, "connectivity checking indicates %s",
-	            nm_connectivity_state_to_string (nm_connectivity_get_state (connectivity)));
+	_LOGD (LOGD_CORE, "connectivity checking indicates %s",
+	       nm_connectivity_state_to_string (nm_connectivity_get_state (connectivity)));
 
 	nm_manager_update_state (self);
 	g_object_notify (G_OBJECT (self), NM_MANAGER_CONNECTIVITY);
@@ -4482,8 +4549,8 @@ firmware_dir_changed (GFileMonitor *monitor,
 	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
 		if (!priv->fw_changed_id) {
 			priv->fw_changed_id = g_timeout_add_seconds (4, handle_firmware_changed, self);
-			nm_log_info (LOGD_CORE, "kernel firmware directory '%s' changed",
-			             KERNEL_FIRMWARE_DIR);
+			_LOGI (LOGD_CORE, "kernel firmware directory '%s' changed",
+			       KERNEL_FIRMWARE_DIR);
 		}
 		break;
 	default:
@@ -4534,7 +4601,7 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
 			g_signal_connect (priv->primary_connection, NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED,
 			                  G_CALLBACK (connection_metered_changed), self);
 		}
-		nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
+		_LOGD (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
 		g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION);
 		g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION_TYPE);
 		nm_manager_update_metered (self);
@@ -4569,7 +4636,7 @@ policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer u
 	if (ac != priv->activating_connection) {
 		g_clear_object (&priv->activating_connection);
 		priv->activating_connection = ac ? g_object_ref (ac) : NULL;
-		nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
+		_LOGD (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_settings_connection_id (ac) : "(none)");
 		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVATING_CONNECTION);
 	}
 }
@@ -4957,9 +5024,9 @@ manager_radio_user_toggled (NMManager *self,
 		return;
 
 	if (rstate->desc) {
-		nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s by user",
-		            rstate->desc,
-		            enabled ? "enabled" : "disabled");
+		_LOGD (LOGD_RFKILL, "(%s): setting radio %s by user",
+		       rstate->desc,
+		       enabled ? "enabled" : "disabled");
 	}
 
 	/* Update enabled key in state file */
@@ -4968,10 +5035,9 @@ manager_radio_user_toggled (NMManager *self,
 		                                "main", rstate->key,
 		                                G_TYPE_BOOLEAN, (gpointer) &enabled,
 		                                &error)) {
-			nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.",
-			             priv->state_file,
-			             error ? error->code : -1,
-			             (error && error->message) ? error->message : "unknown");
+			_LOGW (LOGD_CORE, "writing to state file %s failed: %s",
+			       priv->state_file,
+			       error->message);
 			g_clear_error (&error);
 		}
 	}
@@ -5068,7 +5134,7 @@ nm_manager_setup (const char *state_file,
 	singleton_instance = self;
 
 	nm_singleton_instance_register ();
-	nm_log_dbg (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance);
+	_LOGD (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance);
 
 	nm_exported_object_export ((NMExportedObject *) self);
 
@@ -5139,9 +5205,9 @@ constructed (GObject *object)
 }
 
 static void
-nm_manager_init (NMManager *manager)
+nm_manager_init (NMManager *self)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	guint i;
 	GFile *file;
 
@@ -5173,20 +5239,20 @@ nm_manager_init (NMManager *manager)
 	g_signal_connect (priv->dbus_mgr,
 	                  NM_BUS_MANAGER_DBUS_CONNECTION_CHANGED,
 	                  G_CALLBACK (dbus_connection_changed_cb),
-	                  manager);
+	                  self);
 
 	/* sleep/wake handling */
 	priv->sleep_monitor = g_object_ref (nm_sleep_monitor_get ());
 	g_signal_connect (priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING,
-	                  G_CALLBACK (sleeping_cb), manager);
+	                  G_CALLBACK (sleeping_cb), self);
 	g_signal_connect (priv->sleep_monitor, NM_SLEEP_MONITOR_RESUMING,
-	                  G_CALLBACK (resuming_cb), manager);
+	                  G_CALLBACK (resuming_cb), self);
 
 	/* Listen for authorization changes */
 	g_signal_connect (nm_auth_manager_get (),
 	                  NM_AUTH_MANAGER_SIGNAL_CHANGED,
 	                  G_CALLBACK (authority_changed_cb),
-	                  manager);
+	                  self);
 
 
 	/* Monitor the firmware directory */
@@ -5199,16 +5265,16 @@ nm_manager_init (NMManager *manager)
 	if (priv->fw_monitor) {
 		g_signal_connect (priv->fw_monitor, "changed",
 		                  G_CALLBACK (firmware_dir_changed),
-		                  manager);
-		nm_log_info (LOGD_CORE, "monitoring kernel firmware directory '%s'.",
+		                  self);
+		_LOGI (LOGD_CORE, "monitoring kernel firmware directory '%s'.",
 		             KERNEL_FIRMWARE_DIR);
 	} else {
-		nm_log_warn (LOGD_CORE, "failed to monitor kernel firmware directory '%s'.",
-		             KERNEL_FIRMWARE_DIR);
+		_LOGW (LOGD_CORE, "failed to monitor kernel firmware directory '%s'.",
+		       KERNEL_FIRMWARE_DIR);
 	}
 
 	/* Update timestamps in active connections */
-	priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager);
+	priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, self);
 
 	priv->metered = NM_METERED_UNKNOWN;
 }
@@ -5359,7 +5425,7 @@ set_property (GObject *object, guint prop_id,
 		nm_global_dns_config_free (dns_config);
 
 		if (error) {
-			nm_log_dbg (LOGD_CORE, "set global DNS failed with error: %s", error->message);
+			_LOGD (LOGD_CORE, "set global DNS failed with error: %s", error->message);
 			g_error_free (error);
 		}
 		break;
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 68e50a1c..a46b35a2 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -47,10 +47,18 @@
 #include "nm-dhcp4-config.h"
 #include "nm-dhcp6-config.h"
 
+#define _NMLOG_PREFIX_NAME    "policy"
+#define _NMLOG(level, domain, ...) \
+    G_STMT_START { \
+        nm_log ((level), (domain), \
+                "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+    } G_STMT_END
+
 typedef struct {
 	NMManager *manager;
 	NMFirewallManager *firewall_manager;
-	guint update_state_id;
 	GSList *pending_activation_checks;
 	GSList *manager_ids;
 	GSList *settings_ids;
@@ -82,16 +90,16 @@ typedef struct {
 
 G_DEFINE_TYPE (NMPolicy, nm_policy, G_TYPE_OBJECT)
 
-enum {
-	PROP_0,
-
+NM_GOBJECT_PROPERTIES_DEFINE (NMPolicy,
+	PROP_MANAGER,
+	PROP_SETTINGS,
 	PROP_DEFAULT_IP4_DEVICE,
 	PROP_DEFAULT_IP6_DEVICE,
 	PROP_ACTIVATING_IP4_DEVICE,
-	PROP_ACTIVATING_IP6_DEVICE
-};
+	PROP_ACTIVATING_IP6_DEVICE,
+);
 
-static void schedule_activate_all (NMPolicy *policy);
+static void schedule_activate_all (NMPolicy *self);
 
 
 static NMDevice *
@@ -118,51 +126,38 @@ get_best_ip6_device (NMPolicy *self, gboolean fully_activated)
 
 #define FALLBACK_HOSTNAME4 "localhost.localdomain"
 
-static gboolean
-set_system_hostname (const char *new_hostname, const char *msg)
+static void
+settings_set_hostname_cb (const char *hostname,
+                          gboolean result,
+                          gpointer user_data)
 {
-	char old_hostname[HOST_NAME_MAX + 1];
-	const char *name;
-	int ret;
+	int ret = 0;
 
-	if (new_hostname)
-		g_warn_if_fail (strlen (new_hostname));
+	if (!result) {
+		ret = sethostname (hostname, strlen (hostname));
+		if (ret != 0) {
+			int errsv = errno;
 
-	old_hostname[HOST_NAME_MAX] = '\0';
-	errno = 0;
-	ret = gethostname (old_hostname, HOST_NAME_MAX);
-	if (ret != 0) {
-		nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
-		             errno, strerror (errno));
-	} else {
-		/* Don't set the hostname if it isn't actually changing */
-		if (   (new_hostname && !strcmp (old_hostname, new_hostname))
-		       || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME4)))
-			return FALSE;
-	}
-
-	name = (new_hostname && strlen (new_hostname)) ? new_hostname : FALLBACK_HOSTNAME4;
-
-	nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
-	ret = sethostname (name, strlen (name));
-	if (ret != 0) {
-		int errsv = errno;
-
-		nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
-		             name, errsv, strerror (errsv));
-		if (errsv == EPERM)
-			nm_log_warn (LOGD_DNS, "You should use hostnamed when systemd hardening is in effect!");
+			_LOGW (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
+			       hostname, errsv, strerror (errsv));
+			if (errsv == EPERM)
+				_LOGW (LOGD_DNS, "you should use hostnamed when systemd hardening is in effect!");
+		}
 	}
 
-	return (ret == 0);
+	if (!ret)
+		nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
 }
 
 static void
-_set_hostname (NMPolicy *policy,
+_set_hostname (NMPolicy *self,
                const char *new_hostname,
                const char *msg)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	char old_hostname[HOST_NAME_MAX + 1];
+	const char *name;
+	int ret;
 
 	/* The incoming hostname *can* be NULL, which will get translated to
 	 * 'localhost.localdomain' or such in the hostname policy code, but we
@@ -194,10 +189,41 @@ _set_hostname (NMPolicy *policy,
 	priv->cur_hostname = g_strdup (new_hostname);
 	priv->hostname_changed = TRUE;
 
+	/* Notify the DNS manager of the hostname change so that the domain part, if
+	 * present, can be added to the search list.
+	 */
 	nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
 
-	if (set_system_hostname (priv->cur_hostname, msg))
-		nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
+	 /* Finally, set kernel hostname */
+
+	if (!priv->cur_hostname)
+		name = FALLBACK_HOSTNAME4;
+	else if (!priv->cur_hostname[0]) {
+		g_warn_if_reached ();
+		name = FALLBACK_HOSTNAME4;
+	} else
+		name = priv->cur_hostname;
+
+	old_hostname[HOST_NAME_MAX] = '\0';
+	errno = 0;
+	ret = gethostname (old_hostname, HOST_NAME_MAX);
+	if (ret != 0) {
+		_LOGW (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
+		       errno, strerror (errno));
+	} else {
+		/* Don't set the hostname if it isn't actually changing */
+		if (nm_streq (name, old_hostname))
+			return;
+	}
+
+	_LOGI (LOGD_DNS, "setting system hostname to '%s' (%s)", name, msg);
+
+	/* Ask NMSettings to update the transient hostname using its
+	 * systemd-hostnamed proxy */
+	nm_settings_set_transient_hostname (priv->settings,
+	                                    name,
+	                                    settings_set_hostname_cb,
+	                                    NULL);
 }
 
 static void
@@ -205,8 +231,8 @@ lookup_callback (GObject *source,
                  GAsyncResult *result,
                  gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *hostname;
 	GError *error = NULL;
 
@@ -218,9 +244,9 @@ lookup_callback (GObject *source,
 	}
 
 	if (hostname)
-		_set_hostname (policy, hostname, "from address lookup");
+		_set_hostname (self, hostname, "from address lookup");
 	else {
-		_set_hostname (policy, NULL, error->message);
+		_set_hostname (self, NULL, error->message);
 		g_error_free (error);
 	}
 
@@ -228,15 +254,15 @@ lookup_callback (GObject *source,
 }
 
 static void
-update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
+update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	char *configured_hostname = NULL;
 	const char *dhcp_hostname, *p;
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
 
-	g_return_if_fail (policy != NULL);
+	g_return_if_fail (self != NULL);
 
 	if (priv->lookup_cancellable) {
 		g_cancellable_cancel (priv->lookup_cancellable);
@@ -255,7 +281,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 	/* Try a persistent hostname first */
 	g_object_get (G_OBJECT (priv->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
 	if (configured_hostname && nm_utils_is_specific_hostname (configured_hostname)) {
-		_set_hostname (policy, configured_hostname, "from system configuration");
+		_set_hostname (self, configured_hostname, "from system configuration");
 		g_free (configured_hostname);
 		return;
 	}
@@ -263,15 +289,15 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 
 	/* Try automatically determined hostname from the best device's IP config */
 	if (!best4)
-		best4 = get_best_ip4_device (policy, TRUE);
+		best4 = get_best_ip4_device (self, TRUE);
 	if (!best6)
-		best6 = get_best_ip6_device (policy, TRUE);
+		best6 = get_best_ip6_device (self, TRUE);
 
 	if (!best4 && !best6) {
 		/* No best device; fall back to original hostname or if there wasn't
 		 * one, 'localhost.localdomain'
 		 */
-		_set_hostname (policy, priv->orig_hostname, "no default device");
+		_set_hostname (self, priv->orig_hostname, "no default device");
 		return;
 	}
 
@@ -286,12 +312,12 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 				/* Sanity check; strip leading spaces */
 				while (*p) {
 					if (!g_ascii_isspace (*p++)) {
-						_set_hostname (policy, p-1, "from DHCPv4");
+						_set_hostname (self, p-1, "from DHCPv4");
 						return;
 					}
 				}
-				nm_log_warn (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it",
-				             dhcp_hostname);
+				_LOGW (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it",
+				       dhcp_hostname);
 			}
 		}
 	} else if (best6) {
@@ -305,12 +331,12 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 				/* Sanity check; strip leading spaces */
 				while (*p) {
 					if (!g_ascii_isspace (*p++)) {
-						_set_hostname (policy, p-1, "from DHCPv6");
+						_set_hostname (self, p-1, "from DHCPv6");
 						return;
 					}
 				}
-				nm_log_warn (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it",
-				             dhcp_hostname);
+				_LOGW (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it",
+				       dhcp_hostname);
 			}
 		}
 	}
@@ -319,7 +345,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 	 * when NM started up.
 	 */
 	if (priv->orig_hostname) {
-		_set_hostname (policy, priv->orig_hostname, "from system startup");
+		_set_hostname (self, priv->orig_hostname, "from system startup");
 		return;
 	}
 
@@ -345,7 +371,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 		                                                   G_SOCKET_FAMILY_IPV6);
 	} else {
 		/* No valid IP config; fall back to localhost.localdomain */
-		_set_hostname (policy, NULL, "no IP config");
+		_set_hostname (self, NULL, "no IP config");
 		return;
 	}
 
@@ -353,15 +379,15 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 	g_resolver_lookup_by_address_async (priv->resolver,
 	                                    priv->lookup_addr,
 	                                    priv->lookup_cancellable,
-	                                    lookup_callback, policy);
+	                                    lookup_callback, self);
 }
 
 static void
-update_default_ac (NMPolicy *policy,
+update_default_ac (NMPolicy *self,
                    NMActiveConnection *best,
                    void (*set_active_func)(NMActiveConnection*, gboolean))
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const GSList *connections, *iter;
 
 	/* Clear the 'default[6]' flag on all active connections that aren't the new
@@ -396,14 +422,14 @@ get_best_ip4_config (NMPolicy *self,
 }
 
 static void
-update_ip4_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
+update_ip4_dns (NMPolicy *self, NMDnsManager *dns_mgr)
 {
 	NMIP4Config *ip4_config;
 	const char *ip_iface = NULL;
 	NMVpnConnection *vpn = NULL;
 	NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
 
-	ip4_config = get_best_ip4_config (policy, TRUE, &ip_iface, NULL, NULL, &vpn);
+	ip4_config = get_best_ip4_config (self, TRUE, &ip_iface, NULL, NULL, &vpn);
 	if (ip4_config) {
 		if (vpn)
 			dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
@@ -416,9 +442,9 @@ update_ip4_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
 }
 
 static void
-update_ip4_routing (NMPolicy *policy, gboolean force_update)
+update_ip4_routing (NMPolicy *self, gboolean force_update)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMDevice *best = NULL, *default_device;
 	NMConnection *connection = NULL;
 	NMVpnConnection *vpn = NULL;
@@ -428,13 +454,13 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
 	/* Note that we might have an IPv4 VPN tunneled over an IPv6-only device,
 	 * so we can get (vpn != NULL && best == NULL).
 	 */
-	if (!get_best_ip4_config (policy, FALSE, &ip_iface, &best_ac, &best, &vpn)) {
+	if (!get_best_ip4_config (self, FALSE, &ip_iface, &best_ac, &best, &vpn)) {
 		gboolean changed;
 
 		changed = (priv->default_device4 != NULL);
 		priv->default_device4 = NULL;
 		if (changed)
-			g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE);
+			_notify (self, PROP_DEFAULT_IP4_DEVICE);
 
 		return;
 	}
@@ -462,16 +488,16 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
 	else
 		default_device = best;
 
-	update_default_ac (policy, best_ac, nm_active_connection_set_default);
+	update_default_ac (self, best_ac, nm_active_connection_set_default);
 
 	if (default_device == priv->default_device4)
 		return;
 
 	priv->default_device4 = default_device;
 	connection = nm_active_connection_get_applied_connection (best_ac);
-	nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.",
-	             nm_connection_get_id (connection), ip_iface);
-	g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE);
+	_LOGI (LOGD_CORE, "set '%s' (%s) as default for IPv4 routing and DNS",
+	       nm_connection_get_id (connection), ip_iface);
+	_notify (self, PROP_DEFAULT_IP4_DEVICE);
 }
 
 static NMIP6Config *
@@ -491,14 +517,14 @@ get_best_ip6_config (NMPolicy *self,
 }
 
 static void
-update_ip6_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
+update_ip6_dns (NMPolicy *self, NMDnsManager *dns_mgr)
 {
 	NMIP6Config *ip6_config;
 	const char *ip_iface = NULL;
 	NMVpnConnection *vpn = NULL;
 	NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
 
-	ip6_config = get_best_ip6_config (policy, TRUE, &ip_iface, NULL, NULL, &vpn);
+	ip6_config = get_best_ip6_config (self, TRUE, &ip_iface, NULL, NULL, &vpn);
 	if (ip6_config) {
 		if (vpn)
 			dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
@@ -511,9 +537,9 @@ update_ip6_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
 }
 
 static void
-update_ip6_routing (NMPolicy *policy, gboolean force_update)
+update_ip6_routing (NMPolicy *self, gboolean force_update)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMDevice *best = NULL, *default_device6;
 	NMConnection *connection = NULL;
 	NMVpnConnection *vpn = NULL;
@@ -523,13 +549,13 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
 	/* Note that we might have an IPv6 VPN tunneled over an IPv4-only device,
 	 * so we can get (vpn != NULL && best == NULL).
 	 */
-	if (!get_best_ip6_config (policy, FALSE, &ip_iface, &best_ac, &best, &vpn)) {
+	if (!get_best_ip6_config (self, FALSE, &ip_iface, &best_ac, &best, &vpn)) {
 		gboolean changed;
 
 		changed = (priv->default_device6 != NULL);
 		priv->default_device6 = NULL;
 		if (changed)
-			g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE);
+			_notify (self, PROP_DEFAULT_IP6_DEVICE);
 
 		return;
 	}
@@ -557,56 +583,56 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
 	else
 		default_device6 = best;
 
-	update_default_ac (policy, best_ac, nm_active_connection_set_default6);
+	update_default_ac (self, best_ac, nm_active_connection_set_default6);
 
 	if (default_device6 == priv->default_device6)
 		return;
 
 	priv->default_device6 = default_device6;
 	connection = nm_active_connection_get_applied_connection (best_ac);
-	nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.",
-	             nm_connection_get_id (connection), ip_iface);
-	g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE);
+	_LOGI (LOGD_CORE, "set '%s' (%s) as default for IPv6 routing and DNS",
+	       nm_connection_get_id (connection), ip_iface);
+	_notify (self, PROP_DEFAULT_IP6_DEVICE);
 }
 
 static void
-update_routing_and_dns (NMPolicy *policy, gboolean force_update)
+update_routing_and_dns (NMPolicy *self, gboolean force_update)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
-	update_ip4_dns (policy, priv->dns_manager);
-	update_ip6_dns (policy, priv->dns_manager);
+	update_ip4_dns (self, priv->dns_manager);
+	update_ip6_dns (self, priv->dns_manager);
 
-	update_ip4_routing (policy, force_update);
-	update_ip6_routing (policy, force_update);
+	update_ip4_routing (self, force_update);
+	update_ip6_routing (self, force_update);
 
 	/* Update the system hostname */
-	update_system_hostname (policy, priv->default_device4, priv->default_device6);
+	update_system_hostname (self, priv->default_device4, priv->default_device6);
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
 }
 
 static void
-check_activating_devices (NMPolicy *policy)
+check_activating_devices (NMPolicy *self)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
-	GObject *object = G_OBJECT (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	GObject *object = G_OBJECT (self);
 	NMDevice *best4, *best6 = NULL;
 
-	best4 = get_best_ip4_device (policy, FALSE);
-	best6 = get_best_ip6_device (policy, FALSE);
+	best4 = get_best_ip4_device (self, FALSE);
+	best6 = get_best_ip6_device (self, FALSE);
 
 	g_object_freeze_notify (object);
 
 	if (best4 != priv->activating_device4) {
 		priv->activating_device4 = best4;
-		g_object_notify (object, NM_POLICY_ACTIVATING_IP4_DEVICE);
+		_notify (self, PROP_ACTIVATING_IP4_DEVICE);
 	}
 	if (best6 != priv->activating_device6) {
 		priv->activating_device6 = best6;
-		g_object_notify (object, NM_POLICY_ACTIVATING_IP6_DEVICE);
+		_notify (self, PROP_ACTIVATING_IP6_DEVICE);
 	}
 
 	g_object_thaw_notify (object);
@@ -629,14 +655,15 @@ activate_data_free (ActivateData *data)
 	if (data->autoactivate_id)
 		g_source_remove (data->autoactivate_id);
 	g_object_unref (data->device);
-	g_free (data);
+
+	g_slice_free (ActivateData, data);
 }
 
 static gboolean
 auto_activate_device (gpointer user_data)
 {
 	ActivateData *data = (ActivateData *) user_data;
-	NMPolicy *policy;
+	NMPolicy *self;
 	NMPolicyPrivate *priv;
 	NMSettingsConnection *best_connection;
 	char *specific_object = NULL;
@@ -645,8 +672,8 @@ auto_activate_device (gpointer user_data)
 	guint i;
 
 	g_assert (data);
-	policy = data->policy;
-	priv = NM_POLICY_GET_PRIVATE (policy);
+	self = data->policy;
+	priv = NM_POLICY_GET_PRIVATE (self);
 
 	data->autoactivate_id = 0;
 
@@ -686,8 +713,8 @@ auto_activate_device (gpointer user_data)
 		GError *error = NULL;
 		NMAuthSubject *subject;
 
-		nm_log_info (LOGD_DEVICE, "Auto-activating connection '%s'.",
-		             nm_settings_connection_get_id (best_connection));
+		_LOGI (LOGD_DEVICE, "auto-activating connection '%s'",
+		       nm_settings_connection_get_id (best_connection));
 		subject = nm_auth_subject_new_internal ();
 		if (!nm_manager_activate_connection (priv->manager,
 		                                     best_connection,
@@ -695,10 +722,10 @@ auto_activate_device (gpointer user_data)
 		                                     data->device,
 		                                     subject,
 		                                     &error)) {
-			nm_log_info (LOGD_DEVICE, "Connection '%s' auto-activation failed: (%d) %s",
-			             nm_settings_connection_get_id (best_connection),
-			             error ? error->code : -1,
-			             error ? error->message : "(none)");
+			_LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: (%d) %s",
+			       nm_settings_connection_get_id (best_connection),
+			       error->code,
+			       error->message);
 			g_error_free (error);
 		}
 		g_object_unref (subject);
@@ -733,7 +760,7 @@ pending_secondary_data_new (NMDevice *device, GSList *secondaries)
 {
 	PendingSecondaryData *data;
 
-	data = g_malloc0 (sizeof (PendingSecondaryData));
+	data = g_slice_new (PendingSecondaryData);
 	data->device = g_object_ref (device);
 	data->secondaries = secondaries;
 	return data;
@@ -744,16 +771,15 @@ pending_secondary_data_free (PendingSecondaryData *data)
 {
 	g_object_unref (data->device);
 	g_slist_free_full (data->secondaries, g_object_unref);
-	memset (data, 0, sizeof (*data));
-	g_free (data);
+	g_slice_free (PendingSecondaryData, data);
 }
 
 static void
-process_secondaries (NMPolicy *policy,
+process_secondaries (NMPolicy *self,
                      NMActiveConnection *active,
                      gboolean connected)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	GSList *iter, *iter2, *next, *next2;
 
 	/* Loop through devices waiting for secondary connections to activate */
@@ -773,9 +799,9 @@ process_secondaries (NMPolicy *policy,
 				continue;
 
 			if (connected) {
-				nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' SUCCEEDED; active path '%s'",
-				            nm_active_connection_get_settings_connection_id (active),
-				            nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)));
+				_LOGD (LOGD_DEVICE, "secondary connection '%s' succeeded; active path '%s'",
+				       nm_active_connection_get_settings_connection_id (active),
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)));
 
 				/* Secondary connection activated */
 				secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, secondary_active);
@@ -789,9 +815,9 @@ process_secondaries (NMPolicy *policy,
 					break;
 				}
 			} else {
-				nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' FAILED; active path '%s'",
-				            nm_active_connection_get_settings_connection_id (active),
-				            nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)));
+				_LOGD (LOGD_DEVICE, "secondary connection '%s' failed; active path '%s'",
+				       nm_active_connection_get_settings_connection_id (active),
+				       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active)));
 
 				/* Secondary connection failed -> do not watch other connections */
 				priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data);
@@ -818,16 +844,16 @@ hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 }
 
 static void
-reset_autoconnect_all (NMPolicy *policy, NMDevice *device)
+reset_autoconnect_all (NMPolicy *self, NMDevice *device)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	GSList *connections, *iter;
 
 	if (device) {
-		nm_log_dbg (LOGD_DEVICE, "Re-enabling autoconnect for all connections on %s",
-		            nm_device_get_iface (device));
+		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections on %s",
+		       nm_device_get_iface (device));
 	} else
-		nm_log_dbg (LOGD_DEVICE, "Re-enabling autoconnect for all connections");
+		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections");
 
 	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
@@ -840,12 +866,12 @@ reset_autoconnect_all (NMPolicy *policy, NMDevice *device)
 }
 
 static void
-reset_autoconnect_for_failed_secrets (NMPolicy *policy)
+reset_autoconnect_for_failed_secrets (NMPolicy *self)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	GSList *connections, *iter;
 
-	nm_log_dbg (LOGD_DEVICE, "Re-enabling autoconnect for all connections with failed secrets");
+	_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections with failed secrets");
 
 	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
@@ -860,13 +886,13 @@ reset_autoconnect_for_failed_secrets (NMPolicy *policy)
 }
 
 static void
-block_autoconnect_for_device (NMPolicy *policy, NMDevice *device)
+block_autoconnect_for_device (NMPolicy *self, NMDevice *device)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	GSList *connections, *iter;
 
-	nm_log_dbg (LOGD_DEVICE, "Blocking autoconnect for all connections on %s",
-	            nm_device_get_iface (device));
+	_LOGD (LOGD_DEVICE, "blocking autoconnect for all connections on %s",
+	       nm_device_get_iface (device));
 
 	/* NMDevice keeps its own autoconnect-able-ness state; we only need to
 	 * explicitly block connections for software devices, where the NMDevice
@@ -887,7 +913,7 @@ block_autoconnect_for_device (NMPolicy *policy, NMDevice *device)
 static void
 sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 {
-	NMPolicy *policy = user_data;
+	NMPolicy *self = user_data;
 	gboolean sleeping = FALSE, enabled = FALSE;
 
 	g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL);
@@ -895,13 +921,13 @@ sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 
 	/* Reset retries on all connections so they'll checked on wakeup */
 	if (sleeping || !enabled)
-		reset_autoconnect_all (policy, NULL);
+		reset_autoconnect_all (self, NULL);
 }
 
 static void
-schedule_activate_check (NMPolicy *policy, NMDevice *device)
+schedule_activate_check (NMPolicy *self, NMDevice *device)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	ActivateData *data;
 	const GSList *active_connections, *iter;
 
@@ -925,17 +951,17 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device)
 
 	nm_device_add_pending_action (device, "autoactivate", TRUE);
 
-	data = g_malloc0 (sizeof (ActivateData));
-	data->policy = policy;
+	data = g_slice_new0 (ActivateData);
+	data->policy = self;
 	data->device = g_object_ref (device);
 	data->autoactivate_id = g_idle_add (auto_activate_device, data);
 	priv->pending_activation_checks = g_slist_append (priv->pending_activation_checks, data);
 }
 
 static void
-clear_pending_activate_check (NMPolicy *policy, NMDevice *device)
+clear_pending_activate_check (NMPolicy *self, NMDevice *device)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	ActivateData *data;
 
 	data = find_pending_activation (priv->pending_activation_checks, device);
@@ -946,8 +972,8 @@ clear_pending_activate_check (NMPolicy *policy, NMDevice *device)
 static gboolean
 reset_connections_retries (gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	GSList *connections, *iter;
 	gint32 con_stamp, min_stamp, now;
 	gboolean changed = FALSE;
@@ -974,21 +1000,21 @@ reset_connections_retries (gpointer user_data)
 
 	/* Schedule the handler again if there are some stamps left */
 	if (min_stamp != 0)
-		priv->reset_retries_id = g_timeout_add_seconds (min_stamp - now, reset_connections_retries, policy);
+		priv->reset_retries_id = g_timeout_add_seconds (min_stamp - now, reset_connections_retries, self);
 
 	/* If anything changed, try to activate the newly re-enabled connections */
 	if (changed)
-		schedule_activate_all (policy);
+		schedule_activate_all (self);
 
 	return FALSE;
 }
 
-static void schedule_activate_all (NMPolicy *policy);
+static void schedule_activate_all (NMPolicy *self);
 
 static void
-activate_slave_connections (NMPolicy *policy, NMDevice *device)
+activate_slave_connections (NMPolicy *self, NMDevice *device)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *master_device, *master_uuid_settings = NULL, *master_uuid_applied = NULL;
 	GSList *connections, *iter;
 	NMActRequest *req;
@@ -1034,15 +1060,15 @@ activate_slave_connections (NMPolicy *policy, NMDevice *device)
 
 	g_slist_free (connections);
 
-	schedule_activate_all (policy);
+	schedule_activate_all (self);
 }
 
 static gboolean
-activate_secondary_connections (NMPolicy *policy,
+activate_secondary_connections (NMPolicy *self,
                                 NMConnection *connection,
                                 NMDevice *device)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
 	NMSettingsConnection *settings_con;
 	NMActiveConnection *ac;
@@ -1061,14 +1087,14 @@ activate_secondary_connections (NMPolicy *policy,
 
 		settings_con = nm_settings_get_connection_by_uuid (priv->settings, sec_uuid);
 		if (!settings_con) {
-			nm_log_warn (LOGD_DEVICE, "Secondary connection '%s' auto-activation failed: The connection doesn't exist.",
-			             sec_uuid);
+			_LOGW (LOGD_DEVICE, "secondary connection '%s' auto-activation failed: The connection doesn't exist.",
+			       sec_uuid);
 			success = FALSE;
 			break;
 		}
 		if (!nm_connection_is_type (NM_CONNECTION (settings_con), NM_SETTING_VPN_SETTING_NAME)) {
-			nm_log_warn (LOGD_DEVICE, "Secondary connection '%s (%s)' auto-activation failed: The connection is not a VPN.",
-			             nm_settings_connection_get_id (settings_con), sec_uuid);
+			_LOGW (LOGD_DEVICE, "secondary connection '%s (%s)' auto-activation failed: The connection is not a VPN.",
+			       nm_settings_connection_get_id (settings_con), sec_uuid);
 			success = FALSE;
 			break;
 		}
@@ -1076,9 +1102,9 @@ activate_secondary_connections (NMPolicy *policy,
 		req = nm_device_get_act_request (device);
 		g_assert (req);
 
-		nm_log_dbg (LOGD_DEVICE, "Activating secondary connection '%s (%s)' for base connection '%s (%s)'",
-		            nm_settings_connection_get_id (settings_con), sec_uuid,
-		            nm_connection_get_id (connection), nm_connection_get_uuid (connection));
+		_LOGD (LOGD_DEVICE, "activating secondary connection '%s (%s)' for base connection '%s (%s)'",
+		       nm_settings_connection_get_id (settings_con), sec_uuid,
+		       nm_connection_get_id (connection), nm_connection_get_uuid (connection));
 		ac = nm_manager_activate_connection (priv->manager,
 		                                     settings_con,
 		                                     nm_exported_object_get_path (NM_EXPORTED_OBJECT (req)),
@@ -1088,10 +1114,10 @@ activate_secondary_connections (NMPolicy *policy,
 		if (ac)
 			secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac));
 		else {
-			nm_log_warn (LOGD_DEVICE, "Secondary connection '%s (%s)' auto-activation failed: (%d) %s",
-			             nm_settings_connection_get_id (settings_con), sec_uuid,
-			             error ? error->code : 0,
-			             (error && error->message) ? error->message : "unknown");
+			_LOGW (LOGD_DEVICE, "secondary connection '%s (%s)' auto-activation failed: (%d) %s",
+			       nm_settings_connection_get_id (settings_con), sec_uuid,
+			       error->code,
+			       error->message);
 			g_clear_error (&error);
 			success = FALSE;
 			break;
@@ -1114,8 +1140,8 @@ device_state_changed (NMDevice *device,
                       NMDeviceStateReason reason,
                       gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
 	NMSettingsConnection *connection = nm_device_get_settings_connection (device);
 
@@ -1135,25 +1161,25 @@ device_state_changed (NMDevice *device,
 			guint32 tries = nm_settings_connection_get_autoconnect_retries (connection);
 
 			if (reason == NM_DEVICE_STATE_REASON_NO_SECRETS) {
-				nm_log_dbg (LOGD_DEVICE, "Connection '%s' now blocked from autoconnect due to no secrets",
-				            nm_settings_connection_get_id (connection));
+				_LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to no secrets",
+				       nm_settings_connection_get_id (connection));
 
 				nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_NO_SECRETS);
 			} else if (tries > 0) {
-				nm_log_dbg (LOGD_DEVICE, "Connection '%s' failed to autoconnect; %d tries left",
-				            nm_settings_connection_get_id (connection), tries);
+				_LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; %d tries left",
+				       nm_settings_connection_get_id (connection), tries);
 				nm_settings_connection_set_autoconnect_retries (connection, tries - 1);
 			}
 
 			if (nm_settings_connection_get_autoconnect_retries (connection) == 0) {
-				nm_log_info (LOGD_DEVICE, "Disabling autoconnect for connection '%s'.",
-				             nm_settings_connection_get_id (connection));
+				_LOGI (LOGD_DEVICE, "disabling autoconnect for connection '%s'.",
+				       nm_settings_connection_get_id (connection));
 				/* Schedule a handler to reset retries count */
 				if (!priv->reset_retries_id) {
 					gint32 retry_time = nm_settings_connection_get_autoconnect_retry_time (connection);
 
 					g_warn_if_fail (retry_time != 0);
-					priv->reset_retries_id = g_timeout_add_seconds (MAX (0, retry_time - nm_utils_get_monotonic_timestamp_s ()), reset_connections_retries, policy);
+					priv->reset_retries_id = g_timeout_add_seconds (MAX (0, retry_time - nm_utils_get_monotonic_timestamp_s ()), reset_connections_retries, self);
 				}
 			}
 			nm_connection_clear_secrets (NM_CONNECTION (connection));
@@ -1182,25 +1208,25 @@ device_state_changed (NMDevice *device,
 		if (ip6_config)
 			nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 
-		update_routing_and_dns (policy, FALSE);
+		update_routing_and_dns (self, FALSE);
 
 		nm_dns_manager_end_updates (priv->dns_manager, __func__);
 		break;
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
 		if (old_state > NM_DEVICE_STATE_DISCONNECTED)
-			update_routing_and_dns (policy, FALSE);
+			update_routing_and_dns (self, FALSE);
 		break;
 	case NM_DEVICE_STATE_DEACTIVATING:
 		if (reason == NM_DEVICE_STATE_REASON_USER_REQUESTED) {
 			if (!nm_device_get_autoconnect (device)) {
 				/* The device was disconnected; block all connections on it */
-				block_autoconnect_for_device (policy, device);
+				block_autoconnect_for_device (self, device);
 			} else {
 				if (connection) {
 					/* The connection was deactivated, so block just this connection */
-					nm_log_dbg (LOGD_DEVICE, "Blocking autoconnect of connection '%s' by user request",
-					            nm_settings_connection_get_id (connection));
+					_LOGD (LOGD_DEVICE, "blocking autoconnect of connection '%s' by user request",
+					       nm_settings_connection_get_id (connection));
 					nm_settings_connection_set_autoconnect_blocked_reason (connection,
 					                                                       NM_DEVICE_STATE_REASON_USER_REQUESTED);
 				}
@@ -1212,19 +1238,19 @@ device_state_changed (NMDevice *device,
 		 * was unplugged and plugged in again, we should try to reconnect.
 		 */
 		if (reason == NM_DEVICE_STATE_REASON_CARRIER && old_state == NM_DEVICE_STATE_UNAVAILABLE)
-			reset_autoconnect_all (policy, device);
+			reset_autoconnect_all (self, device);
 
 		if (old_state > NM_DEVICE_STATE_DISCONNECTED)
-			update_routing_and_dns (policy, FALSE);
+			update_routing_and_dns (self, FALSE);
 
 		/* Device is now available for auto-activation */
-		schedule_activate_check (policy, device);
+		schedule_activate_check (self, device);
 		break;
 
 	case NM_DEVICE_STATE_PREPARE:
 		/* Reset auto-connect retries of all slaves and schedule them for
 		 * activation. */
-		activate_slave_connections (policy, device);
+		activate_slave_connections (self, device);
 		break;
 	case NM_DEVICE_STATE_IP_CONFIG:
 		/* We must have secrets if we got here. */
@@ -1236,10 +1262,10 @@ device_state_changed (NMDevice *device,
 			s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 		if (s_con && nm_setting_connection_get_num_secondaries (s_con) > 0) {
 			/* Make routes and DNS up-to-date before activating dependent connections */
-			update_routing_and_dns (policy, FALSE);
+			update_routing_and_dns (self, FALSE);
 
 			/* Activate secondary (VPN) connections */
-			if (!activate_secondary_connections (policy, NM_CONNECTION (connection), device))
+			if (!activate_secondary_connections (self, NM_CONNECTION (connection), device))
 				nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
 				                       NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED);
 		} else
@@ -1251,7 +1277,7 @@ device_state_changed (NMDevice *device,
 		break;
 	}
 
-	check_activating_devices (policy);
+	check_activating_devices (self);
 }
 
 static void
@@ -1260,8 +1286,8 @@ device_ip4_config_changed (NMDevice *device,
                            NMIP4Config *old_config,
                            gpointer user_data)
 {
-	NMPolicy *policy = user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
@@ -1277,8 +1303,8 @@ device_ip4_config_changed (NMDevice *device,
 			if (new_config)
 				nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		}
-		update_ip4_dns (policy, priv->dns_manager);
-		update_ip4_routing (policy, TRUE);
+		update_ip4_dns (self, priv->dns_manager);
+		update_ip4_routing (self, TRUE);
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
@@ -1294,8 +1320,8 @@ device_ip6_config_changed (NMDevice *device,
                            NMIP6Config *old_config,
                            gpointer user_data)
 {
-	NMPolicy *policy = user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
@@ -1311,8 +1337,8 @@ device_ip6_config_changed (NMDevice *device,
 			if (new_config)
 				nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		}
-		update_ip6_dns (policy, priv->dns_manager);
-		update_ip6_routing (policy, TRUE);
+		update_ip6_dns (self, priv->dns_manager);
+		update_ip6_routing (self, TRUE);
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
@@ -1343,21 +1369,21 @@ typedef struct {
 } DeviceSignalId;
 
 static void
-_connect_device_signal (NMPolicy *policy,
+_connect_device_signal (NMPolicy *self,
                         NMDevice *device,
                         const char *name,
                         gpointer callback,
                         gboolean after)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	DeviceSignalId *data;
 
 	data = g_slice_new0 (DeviceSignalId);
 	g_assert (data);
 	if (after)
-		data->id = g_signal_connect_after (device, name, callback, policy);
+		data->id = g_signal_connect_after (device, name, callback, self);
 	else
-		data->id = g_signal_connect (device, name, callback, policy);
+		data->id = g_signal_connect (device, name, callback, self);
 	data->device = device;
 	priv->dev_ids = g_slist_prepend (priv->dev_ids, data);
 }
@@ -1365,25 +1391,25 @@ _connect_device_signal (NMPolicy *policy,
 static void
 device_added (NMManager *manager, NMDevice *device, gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicy *self = (NMPolicy *) user_data;
 
 	/* Connect state-changed with _after, so that the handler is invoked after other handlers. */
-	_connect_device_signal (policy, device, NM_DEVICE_STATE_CHANGED, device_state_changed, TRUE);
-	_connect_device_signal (policy, device, NM_DEVICE_IP4_CONFIG_CHANGED, device_ip4_config_changed, FALSE);
-	_connect_device_signal (policy, device, NM_DEVICE_IP6_CONFIG_CHANGED, device_ip6_config_changed, FALSE);
-	_connect_device_signal (policy, device, "notify::" NM_DEVICE_AUTOCONNECT, device_autoconnect_changed, FALSE);
-	_connect_device_signal (policy, device, NM_DEVICE_RECHECK_AUTO_ACTIVATE, device_recheck_auto_activate, FALSE);
+	_connect_device_signal (self, device, NM_DEVICE_STATE_CHANGED, device_state_changed, TRUE);
+	_connect_device_signal (self, device, NM_DEVICE_IP4_CONFIG_CHANGED, device_ip4_config_changed, FALSE);
+	_connect_device_signal (self, device, NM_DEVICE_IP6_CONFIG_CHANGED, device_ip6_config_changed, FALSE);
+	_connect_device_signal (self, device, "notify::" NM_DEVICE_AUTOCONNECT, device_autoconnect_changed, FALSE);
+	_connect_device_signal (self, device, NM_DEVICE_RECHECK_AUTO_ACTIVATE, device_recheck_auto_activate, FALSE);
 }
 
 static void
 device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	GSList *iter;
 
 	/* Clear any idle callbacks for this device */
-	clear_pending_activate_check (policy, device);
+	clear_pending_activate_check (self, device);
 
 	/* Clear any signal handlers for this device */
 	iter = priv->dev_ids;
@@ -1407,9 +1433,9 @@ device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
 /**************************************************************************/
 
 static void
-vpn_connection_activated (NMPolicy *policy, NMVpnConnection *vpn)
+vpn_connection_activated (NMPolicy *self, NMVpnConnection *vpn)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
 	const char *ip_iface;
@@ -1428,15 +1454,15 @@ vpn_connection_activated (NMPolicy *policy, NMVpnConnection *vpn)
 	if (ip6_config)
 		nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_VPN);
 
-	update_routing_and_dns (policy, TRUE);
+	update_routing_and_dns (self, TRUE);
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
 }
 
 static void
-vpn_connection_deactivated (NMPolicy *policy, NMVpnConnection *vpn)
+vpn_connection_deactivated (NMPolicy *self, NMVpnConnection *vpn)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
 
@@ -1454,7 +1480,7 @@ vpn_connection_deactivated (NMPolicy *policy, NMVpnConnection *vpn)
 		nm_dns_manager_remove_ip6_config (priv->dns_manager, ip6_config);
 	}
 
-	update_routing_and_dns (policy, TRUE);
+	update_routing_and_dns (self, TRUE);
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
 }
@@ -1464,22 +1490,22 @@ vpn_connection_state_changed (NMVpnConnection *vpn,
                               NMVpnConnectionState new_state,
                               NMVpnConnectionState old_state,
                               NMVpnConnectionStateReason reason,
-                              NMPolicy *policy)
+                              NMPolicy *self)
 {
 	if (new_state == NM_VPN_CONNECTION_STATE_ACTIVATED)
-		vpn_connection_activated (policy, vpn);
+		vpn_connection_activated (self, vpn);
 	else if (new_state >= NM_VPN_CONNECTION_STATE_FAILED) {
 		/* Only clean up IP/DNS if the connection ever got past IP_CONFIG */
 		if (old_state >= NM_VPN_CONNECTION_STATE_IP_CONFIG_GET &&
 		    old_state <= NM_VPN_CONNECTION_STATE_ACTIVATED)
-			vpn_connection_deactivated (policy, vpn);
+			vpn_connection_deactivated (self, vpn);
 	}
 }
 
 static void
-vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *policy)
+vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMActiveConnection *ac = NM_ACTIVE_CONNECTION (vpn);
 	NMSettingsConnection *connection = nm_active_connection_get_settings_connection (ac);
 	GError *error = NULL;
@@ -1491,9 +1517,9 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *policy)
 	                                     NULL,
 	                                     nm_active_connection_get_subject (ac),
 	                                     &error)) {
-		nm_log_warn (LOGD_DEVICE, "VPN '%s' reconnect failed: %s",
-		             nm_settings_connection_get_id (connection),
-		             error->message ? error->message : "unknown");
+		_LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s",
+		       nm_settings_connection_get_id (connection),
+		       error->message ? error->message : "unknown");
 		g_clear_error (&error);
 	}
 }
@@ -1501,14 +1527,14 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *policy)
 static void
 active_connection_state_changed (NMActiveConnection *active,
                                  GParamSpec *pspec,
-                                 NMPolicy *policy)
+                                 NMPolicy *self)
 {
 	NMActiveConnectionState state = nm_active_connection_get_state (active);
 
 	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
-		process_secondaries (policy, active, TRUE);
+		process_secondaries (self, active, TRUE);
 	else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
-		process_secondaries (policy, active, FALSE);
+		process_secondaries (self, active, FALSE);
 }
 
 static void
@@ -1516,20 +1542,20 @@ active_connection_added (NMManager *manager,
                          NMActiveConnection *active,
                          gpointer user_data)
 {
-	NMPolicy *policy = NM_POLICY (user_data);
+	NMPolicy *self = NM_POLICY (user_data);
 
 	if (NM_IS_VPN_CONNECTION (active)) {
 		g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
 		                  G_CALLBACK (vpn_connection_state_changed),
-		                  policy);
+		                  self);
 		g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_RETRY_AFTER_FAILURE,
 		                  G_CALLBACK (vpn_connection_retry_after_failure),
-		                  policy);
+		                  self);
 	}
 
 	g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE,
 	                  G_CALLBACK (active_connection_state_changed),
-	                  policy);
+	                  self);
 }
 
 static void
@@ -1537,29 +1563,29 @@ active_connection_removed (NMManager *manager,
                            NMActiveConnection *active,
                            gpointer user_data)
 {
-	NMPolicy *policy = NM_POLICY (user_data);
+	NMPolicy *self = NM_POLICY (user_data);
 
 	g_signal_handlers_disconnect_by_func (active,
 	                                      vpn_connection_state_changed,
-	                                      policy);
+	                                      self);
 	g_signal_handlers_disconnect_by_func (active,
 	                                      vpn_connection_retry_after_failure,
-	                                      policy);
+	                                      self);
 	g_signal_handlers_disconnect_by_func (active,
 	                                      active_connection_state_changed,
-	                                      policy);
+	                                      self);
 }
 
 /**************************************************************************/
 
 static void
-schedule_activate_all (NMPolicy *policy)
+schedule_activate_all (NMPolicy *self)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const GSList *iter;
 
 	for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter))
-		schedule_activate_check (policy, NM_DEVICE (iter->data));
+		schedule_activate_check (self, NM_DEVICE (iter->data));
 }
 
 static void
@@ -1567,17 +1593,17 @@ connection_added (NMSettings *settings,
                   NMSettingsConnection *connection,
                   gpointer user_data)
 {
-	NMPolicy *policy = NM_POLICY (user_data);
+	NMPolicy *self = NM_POLICY (user_data);
 
-	schedule_activate_all (policy);
+	schedule_activate_all (self);
 }
 
 static void
 firewall_started (NMFirewallManager *manager,
                   gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const GSList *iter;
 
 	/* add interface of each device to correct zone */
@@ -1588,8 +1614,8 @@ firewall_started (NMFirewallManager *manager,
 static void
 dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
 	/* Restart a thread for reverse-DNS lookup after we are signalled that
 	 * DNS changed. Because the result from a previous run may not be right
@@ -1606,15 +1632,15 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
 	if (priv->lookup_addr) {
 		char *str = NULL;
 
-		nm_log_dbg (LOGD_DNS, "restarting reverse-lookup thread for address %s",
-		            (str = g_inet_address_to_string (priv->lookup_addr)));
+		_LOGD (LOGD_DNS, "restarting reverse-lookup thread for address %s",
+		       (str = g_inet_address_to_string (priv->lookup_addr)));
 		g_free (str);
 
 		priv->lookup_cancellable = g_cancellable_new ();
 		g_resolver_lookup_by_address_async (priv->resolver,
 		                                    priv->lookup_addr,
 		                                    priv->lookup_cancellable,
-		                                    lookup_callback, policy);
+		                                    lookup_callback, self);
 	}
 }
 
@@ -1631,8 +1657,8 @@ connection_updated_by_user (NMSettings *settings,
                             NMSettingsConnection *connection,
                             gpointer user_data)
 {
-	NMPolicy *policy = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const GSList *iter;
 	NMDevice *device = NULL;
 
@@ -1670,10 +1696,10 @@ _deactivate_if_active (NMManager *manager, NMSettingsConnection *connection)
 			                                       nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac)),
 			                                       NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
 			                                       &error)) {
-				nm_log_warn (LOGD_DEVICE, "Connection '%s' disappeared, but error deactivating it: (%d) %s",
-					         nm_settings_connection_get_id (connection),
-					         error ? error->code : -1,
-					         error ? error->message : "(unknown)");
+				_LOGW (LOGD_DEVICE, "connection '%s' disappeared, but error deactivating it: (%d) %s",
+				       nm_settings_connection_get_id (connection),
+				       error ? error->code : -1,
+				       error ? error->message : "(unknown)");
 				g_clear_error (&error);
 			}
 		}
@@ -1685,8 +1711,8 @@ connection_removed (NMSettings *settings,
                     NMSettingsConnection *connection,
                     gpointer user_data)
 {
-	NMPolicy *policy = user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
 	_deactivate_if_active (priv->manager, connection);
 }
@@ -1696,11 +1722,11 @@ connection_visibility_changed (NMSettings *settings,
                                NMSettingsConnection *connection,
                                gpointer user_data)
 {
-	NMPolicy *policy = user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
 	if (nm_settings_connection_is_visible (connection))
-		schedule_activate_all (policy);
+		schedule_activate_all (self);
 	else
 		_deactivate_if_active (priv->manager, connection);
 }
@@ -1710,52 +1736,125 @@ secret_agent_registered (NMSettings *settings,
                          NMSecretAgent *agent,
                          gpointer user_data)
 {
-	NMPolicy *policy = NM_POLICY (user_data);
+	NMPolicy *self = NM_POLICY (user_data);
 
 	/* The registered secret agent may provide some missing secrets. Thus we
 	 * reset retries count here and schedule activation, so that the
 	 * connections failed due to missing secrets may re-try auto-connection.
 	 */
-	reset_autoconnect_for_failed_secrets (policy);
-	schedule_activate_all (policy);
+	reset_autoconnect_for_failed_secrets (self);
+	schedule_activate_all (self);
 }
 
+NMDevice *
+nm_policy_get_default_ip4_device (NMPolicy *self)
+{
+	return NM_POLICY_GET_PRIVATE (self)->default_device4;
+}
+
+NMDevice *
+nm_policy_get_default_ip6_device (NMPolicy *self)
+{
+	return NM_POLICY_GET_PRIVATE (self)->default_device6;
+}
+
+NMDevice *
+nm_policy_get_activating_ip4_device (NMPolicy *self)
+{
+	return NM_POLICY_GET_PRIVATE (self)->activating_device4;
+}
+
+NMDevice *
+nm_policy_get_activating_ip6_device (NMPolicy *self)
+{
+	return NM_POLICY_GET_PRIVATE (self)->activating_device6;
+}
+
+/*****************************************************************************/
+
 static void
-_connect_manager_signal (NMPolicy *policy, const char *name, gpointer callback)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = NM_POLICY (object);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_DEFAULT_IP4_DEVICE:
+		g_value_set_object (value, priv->default_device4);
+		break;
+	case PROP_DEFAULT_IP6_DEVICE:
+		g_value_set_object (value, priv->default_device6);
+		break;
+	case PROP_ACTIVATING_IP4_DEVICE:
+		g_value_set_object (value, priv->activating_device4);
+		break;
+	case PROP_ACTIVATING_IP6_DEVICE:
+		g_value_set_object (value, priv->activating_device6);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMPolicy *self = NM_POLICY (object);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_MANAGER:
+		/* construct-only */
+		priv->manager = g_value_get_object (value);
+		g_return_if_fail (NM_IS_MANAGER (priv->manager));
+		break;
+	case PROP_SETTINGS:
+		/* construct-only */
+		priv->settings = g_value_dup_object (value);
+		g_return_if_fail (NM_IS_SETTINGS (priv->settings));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+_connect_manager_signal (NMPolicy *self, const char *name, gpointer callback)
+{
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	gulong id;
 
-	id = g_signal_connect (priv->manager, name, callback, policy);
+	id = g_signal_connect (priv->manager, name, callback, self);
 	priv->manager_ids = g_slist_prepend (priv->manager_ids, (gpointer) id);
 }
 
 static void
-_connect_settings_signal (NMPolicy *policy, const char *name, gpointer callback)
+_connect_settings_signal (NMPolicy *self, const char *name, gpointer callback)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	gulong id;
 
-	id = g_signal_connect (priv->settings, name, callback, policy);
+	id = g_signal_connect (priv->settings, name, callback, self);
 	priv->settings_ids = g_slist_prepend (priv->settings_ids, (gpointer) id);
 }
 
-NMPolicy *
-nm_policy_new (NMManager *manager, NMSettings *settings)
+static void
+nm_policy_init (NMPolicy *self)
 {
-	NMPolicy *policy;
-	NMPolicyPrivate *priv;
-	static gboolean initialized = FALSE;
-	char hostname[HOST_NAME_MAX + 2];
-
-	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
-	g_return_val_if_fail (initialized == FALSE, NULL);
+}
 
-	policy = g_object_new (NM_TYPE_POLICY, NULL);
-	priv = NM_POLICY_GET_PRIVATE (policy);
-	priv->manager = manager;
-	priv->settings = g_object_ref (settings);
-	priv->update_state_id = 0;
+static void
+constructed (GObject *object)
+{
+	NMPolicy *self = NM_POLICY (object);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	char hostname[HOST_NAME_MAX + 2];
 
 	/* Grab hostname on startup and use that if nothing provides one */
 	memset (hostname, 0, sizeof (hostname));
@@ -1768,103 +1867,56 @@ nm_policy_new (NMManager *manager, NMSettings *settings)
 	priv->firewall_manager = g_object_ref (nm_firewall_manager_get ());
 
 	priv->fw_started_id = g_signal_connect (priv->firewall_manager, "started",
-	                                        G_CALLBACK (firewall_started), policy);
+	                                        G_CALLBACK (firewall_started), self);
 
 	priv->dns_manager = g_object_ref (nm_dns_manager_get ());
 	nm_dns_manager_set_initial_hostname (priv->dns_manager, priv->orig_hostname);
 	priv->config_changed_id = g_signal_connect (priv->dns_manager, "config-changed",
-	                                            G_CALLBACK (dns_config_changed), policy);
+	                                            G_CALLBACK (dns_config_changed), self);
 
 	priv->resolver = g_resolver_get_default ();
 
-	_connect_manager_signal (policy, NM_MANAGER_STATE_CHANGED, global_state_changed);
-	_connect_manager_signal (policy, "notify::" NM_MANAGER_HOSTNAME, hostname_changed);
-	_connect_manager_signal (policy, "notify::" NM_MANAGER_SLEEPING, sleeping_changed);
-	_connect_manager_signal (policy, "notify::" NM_MANAGER_NETWORKING_ENABLED, sleeping_changed);
-	_connect_manager_signal (policy, "internal-device-added", device_added);
-	_connect_manager_signal (policy, "internal-device-removed", device_removed);
-	_connect_manager_signal (policy, NM_MANAGER_ACTIVE_CONNECTION_ADDED, active_connection_added);
-	_connect_manager_signal (policy, NM_MANAGER_ACTIVE_CONNECTION_REMOVED, active_connection_removed);
-
-	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, connection_added);
-	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, connection_updated);
-	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER, connection_updated_by_user);
-	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, connection_removed);
-	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
+	_connect_manager_signal (self, NM_MANAGER_STATE_CHANGED, global_state_changed);
+	_connect_manager_signal (self, "notify::" NM_MANAGER_HOSTNAME, hostname_changed);
+	_connect_manager_signal (self, "notify::" NM_MANAGER_SLEEPING, sleeping_changed);
+	_connect_manager_signal (self, "notify::" NM_MANAGER_NETWORKING_ENABLED, sleeping_changed);
+	_connect_manager_signal (self, "internal-device-added", device_added);
+	_connect_manager_signal (self, "internal-device-removed", device_removed);
+	_connect_manager_signal (self, NM_MANAGER_ACTIVE_CONNECTION_ADDED, active_connection_added);
+	_connect_manager_signal (self, NM_MANAGER_ACTIVE_CONNECTION_REMOVED, active_connection_removed);
+
+	_connect_settings_signal (self, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, connection_added);
+	_connect_settings_signal (self, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, connection_updated);
+	_connect_settings_signal (self, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER, connection_updated_by_user);
+	_connect_settings_signal (self, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, connection_removed);
+	_connect_settings_signal (self, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
 	                          connection_visibility_changed);
-	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, secret_agent_registered);
+	_connect_settings_signal (self, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, secret_agent_registered);
 
-	initialized = TRUE;
-	return policy;
+	G_OBJECT_CLASS (nm_policy_parent_class)->constructed (object);
 }
 
-NMDevice *
-nm_policy_get_default_ip4_device (NMPolicy *policy)
-{
-	return NM_POLICY_GET_PRIVATE (policy)->default_device4;
-}
-
-NMDevice *
-nm_policy_get_default_ip6_device (NMPolicy *policy)
-{
-	return NM_POLICY_GET_PRIVATE (policy)->default_device6;
-}
-
-NMDevice *
-nm_policy_get_activating_ip4_device (NMPolicy *policy)
-{
-	return NM_POLICY_GET_PRIVATE (policy)->activating_device4;
-}
-
-NMDevice *
-nm_policy_get_activating_ip6_device (NMPolicy *policy)
-{
-	return NM_POLICY_GET_PRIVATE (policy)->activating_device6;
-}
-
-static void
-nm_policy_init (NMPolicy *policy)
-{
-}
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+NMPolicy *
+nm_policy_new (NMManager *manager, NMSettings *settings)
 {
-	NMPolicy *policy = NM_POLICY (object);
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
 
-	switch (prop_id) {
-	case PROP_DEFAULT_IP4_DEVICE:
-		g_value_set_object (value, priv->default_device4);
-		break;
-	case PROP_DEFAULT_IP6_DEVICE:
-		g_value_set_object (value, priv->default_device6);
-		break;
-	case PROP_ACTIVATING_IP4_DEVICE:
-		g_value_set_object (value, priv->activating_device4);
-		break;
-	case PROP_ACTIVATING_IP6_DEVICE:
-		g_value_set_object (value, priv->activating_device6);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	return g_object_new (NM_TYPE_POLICY,
+	                     NM_POLICY_MANAGER, manager,
+	                     NM_POLICY_SETTINGS, settings,
+	                     NULL);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMPolicy *policy = NM_POLICY (object);
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	NMPolicy *self = NM_POLICY (object);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const GSList *connections, *iter;
 
-	/* Tell any existing hostname lookup thread to die. */
-	if (priv->lookup_cancellable) {
-		g_cancellable_cancel (priv->lookup_cancellable);
-		g_clear_object (&priv->lookup_cancellable);
-	}
+	nm_clear_g_cancellable (&priv->lookup_cancellable);
+
 	g_clear_object (&priv->lookup_addr);
 	g_clear_object (&priv->resolver);
 
@@ -1876,13 +1928,12 @@ dispose (GObject *object)
 
 	if (priv->firewall_manager) {
 		g_assert (priv->fw_started_id);
-		g_signal_handler_disconnect (priv->firewall_manager, priv->fw_started_id);
-		priv->fw_started_id = 0;
+		nm_clear_g_signal_handler (priv->firewall_manager, &priv->fw_started_id);
 		g_clear_object (&priv->firewall_manager);
 	}
 
 	if (priv->dns_manager) {
-		g_signal_handler_disconnect (priv->dns_manager, priv->config_changed_id);
+		nm_clear_g_signal_handler (priv->dns_manager, &priv->config_changed_id);
 		g_clear_object (&priv->dns_manager);
 	}
 
@@ -1916,6 +1967,8 @@ dispose (GObject *object)
 
 	g_clear_object (&priv->settings);
 
+	nm_assert (NM_IS_MANAGER (priv->manager));
+
 	G_OBJECT_CLASS (nm_policy_parent_class)->dispose (object);
 }
 
@@ -1927,30 +1980,41 @@ nm_policy_class_init (NMPolicyClass *policy_class)
 	g_type_class_add_private (policy_class, sizeof (NMPolicyPrivate));
 
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 
-	g_object_class_install_property
-		(object_class, PROP_DEFAULT_IP4_DEVICE,
-		 g_param_spec_object (NM_POLICY_DEFAULT_IP4_DEVICE, "", "",
-		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property
-		(object_class, PROP_DEFAULT_IP6_DEVICE,
-		 g_param_spec_object (NM_POLICY_DEFAULT_IP6_DEVICE, "", "",
-		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property
-		(object_class, PROP_ACTIVATING_IP4_DEVICE,
-		 g_param_spec_object (NM_POLICY_ACTIVATING_IP4_DEVICE, "", "",
-		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property
-		(object_class, PROP_ACTIVATING_IP6_DEVICE,
-		 g_param_spec_object (NM_POLICY_ACTIVATING_IP6_DEVICE, "", "",
-		                      NM_TYPE_DEVICE,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MANAGER] =
+	    g_param_spec_object (NM_POLICY_MANAGER, "", "",
+	                         NM_TYPE_MANAGER,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_SETTINGS] =
+	    g_param_spec_object (NM_POLICY_SETTINGS, "", "",
+	                         NM_TYPE_SETTINGS,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DEFAULT_IP4_DEVICE] =
+	    g_param_spec_object (NM_POLICY_DEFAULT_IP4_DEVICE, "", "",
+	                         NM_TYPE_DEVICE,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_DEFAULT_IP6_DEVICE] =
+	    g_param_spec_object (NM_POLICY_DEFAULT_IP6_DEVICE, "", "",
+	                         NM_TYPE_DEVICE,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ACTIVATING_IP4_DEVICE] =
+	    g_param_spec_object (NM_POLICY_ACTIVATING_IP4_DEVICE, "", "",
+	                         NM_TYPE_DEVICE,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_ACTIVATING_IP6_DEVICE] =
+	    g_param_spec_object (NM_POLICY_ACTIVATING_IP6_DEVICE, "", "",
+	                         NM_TYPE_DEVICE,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/src/nm-policy.h b/src/nm-policy.h
index 7824f3ea..4d504cdd 100644
--- a/src/nm-policy.h
+++ b/src/nm-policy.h
@@ -31,8 +31,10 @@
 #define NM_IS_POLICY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_POLICY))
 #define NM_POLICY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_POLICY, NMPolicyClass))
 
-#define NM_POLICY_DEFAULT_IP4_DEVICE "default-ip4-device"
-#define NM_POLICY_DEFAULT_IP6_DEVICE "default-ip6-device"
+#define NM_POLICY_MANAGER               "manager"
+#define NM_POLICY_SETTINGS              "settings"
+#define NM_POLICY_DEFAULT_IP4_DEVICE    "default-ip4-device"
+#define NM_POLICY_DEFAULT_IP6_DEVICE    "default-ip6-device"
 #define NM_POLICY_ACTIVATING_IP4_DEVICE "activating-ip4-device"
 #define NM_POLICY_ACTIVATING_IP6_DEVICE "activating-ip6-device"
 
diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c
index 84811d5a..3cf24118 100644
--- a/src/nm-route-manager.c
+++ b/src/nm-route-manager.c
@@ -73,6 +73,10 @@ typedef struct {
 
 G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT);
 
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PLATFORM,
+);
+
 NM_DEFINE_SINGLETON_GETTER (NMRouteManager, nm_route_manager_get, NM_TYPE_ROUTE_MANAGER);
 
 /*********************************************************************************************/
@@ -1139,11 +1143,30 @@ static const VTableIP vtable_v6 = {
 /*********************************************************************************************/
 
 static void
-nm_route_manager_init (NMRouteManager *self)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
+	NMRouteManager *self = NM_ROUTE_MANAGER (object);
 	NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
 
-	priv->platform = g_object_ref (NM_PLATFORM_GET);
+	switch (prop_id) {
+	case PROP_PLATFORM:
+		/* construct-only */
+		priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
+		if (!priv->platform)
+			g_return_if_reached ();
+		g_object_ref (priv->platform);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_route_manager_init (NMRouteManager *self)
+{
+	NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
 
 	priv->ip4_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
 	priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
@@ -1159,6 +1182,14 @@ nm_route_manager_init (NMRouteManager *self)
 	                                                         (GDestroyNotify) _ip4_device_routes_purge_entry_free);
 }
 
+NMRouteManager *
+nm_route_manager_new (NMPlatform *platform)
+{
+	return g_object_new (NM_TYPE_ROUTE_MANAGER,
+	                     NM_ROUTE_MANAGER_PLATFORM, platform,
+	                     NULL);
+}
+
 static void
 dispose (GObject *object)
 {
@@ -1168,8 +1199,6 @@ dispose (GObject *object)
 	g_hash_table_remove_all (priv->ip4_device_routes.entries);
 	_ip4_device_routes_cancel (self);
 
-	g_clear_object (&priv->platform);
-
 	G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object);
 }
 
@@ -1189,6 +1218,8 @@ finalize (GObject *object)
 
 	g_hash_table_unref (priv->ip4_device_routes.entries);
 
+	g_clear_object (&priv->platform);
+
 	G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object);
 }
 
@@ -1200,6 +1231,15 @@ nm_route_manager_class_init (NMRouteManagerClass *klass)
 	g_type_class_add_private (klass, sizeof (NMRouteManagerPrivate));
 
 	/* virtual methods */
+	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
+
+	obj_properties[PROP_PLATFORM] =
+	    g_param_spec_object (NM_ROUTE_MANAGER_PLATFORM, "", "",
+	                         NM_TYPE_PLATFORM,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h
index 9a08496f..1f4b8174 100644
--- a/src/nm-route-manager.h
+++ b/src/nm-route-manager.h
@@ -31,6 +31,8 @@
 #define NM_IS_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ROUTE_MANAGER))
 #define NM_ROUTE_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass))
 
+#define NM_ROUTE_MANAGER_PLATFORM "platform"
+
 struct _NMRouteManager {
 	GObject parent;
 };
@@ -48,5 +50,6 @@ gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex);
 void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list);
 
 NMRouteManager *nm_route_manager_get (void);
+NMRouteManager *nm_route_manager_new (NMPlatform *platform);
 
 #endif  /* NM_ROUTE_MANAGER_H */
diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c
index d8b046e6..46709427 100644
--- a/src/nm-session-monitor.c
+++ b/src/nm-session-monitor.c
@@ -80,7 +80,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 #ifdef SESSION_TRACKING_SYSTEMD
 static gboolean
-sd_session_exists (NMSessionMonitor *monitor, uid_t uid, gboolean active)
+st_sd_session_exists (NMSessionMonitor *monitor, uid_t uid, gboolean active)
 {
 	int status;
 
@@ -97,7 +97,7 @@ sd_session_exists (NMSessionMonitor *monitor, uid_t uid, gboolean active)
 }
 
 static gboolean
-sd_changed (GIOChannel *stream, GIOCondition condition, gpointer user_data)
+st_sd_changed (GIOChannel *stream, GIOCondition condition, gpointer user_data)
 {
 	NMSessionMonitor *monitor = user_data;
 
@@ -109,7 +109,7 @@ sd_changed (GIOChannel *stream, GIOCondition condition, gpointer user_data)
 }
 
 static void
-sd_init (NMSessionMonitor *monitor)
+st_sd_init (NMSessionMonitor *monitor)
 {
 	int status;
 	GIOChannel *stream;
@@ -123,13 +123,13 @@ sd_init (NMSessionMonitor *monitor)
 	}
 
 	stream = g_io_channel_unix_new (sd_login_monitor_get_fd (monitor->sd.monitor));
-	monitor->sd.watch = g_io_add_watch (stream, G_IO_IN, sd_changed, monitor);
+	monitor->sd.watch = g_io_add_watch (stream, G_IO_IN, st_sd_changed, monitor);
 
 	g_io_channel_unref (stream);
 }
 
 static void
-sd_finalize (NMSessionMonitor *monitor)
+st_sd_finalize (NMSessionMonitor *monitor)
 {
 	g_clear_pointer (&monitor->sd.monitor, sd_login_monitor_unref);
 	g_source_remove (monitor->sd.watch);
@@ -383,7 +383,7 @@ nm_session_monitor_session_exists (NMSessionMonitor *self,
 	g_return_val_if_fail (NM_IS_SESSION_MONITOR (self), FALSE);
 
 #ifdef SESSION_TRACKING_SYSTEMD
-	if (sd_session_exists (self, uid, active))
+	if (st_sd_session_exists (self, uid, active))
 		return TRUE;
 #endif
 
@@ -401,7 +401,7 @@ static void
 nm_session_monitor_init (NMSessionMonitor *monitor)
 {
 #ifdef SESSION_TRACKING_SYSTEMD
-	sd_init (monitor);
+	st_sd_init (monitor);
 #endif
 
 #ifdef SESSION_TRACKING_CONSOLEKIT
@@ -413,7 +413,7 @@ static void
 nm_session_monitor_finalize (GObject *object)
 {
 #ifdef SESSION_TRACKING_SYSTEMD
-	sd_finalize (NM_SESSION_MONITOR (object));
+	st_sd_finalize (NM_SESSION_MONITOR (object));
 #endif
 
 #ifdef SESSION_TRACKING_CONSOLEKIT
diff --git a/src/nm-types.h b/src/nm-types.h
index 0fda9216..997723e6 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -74,12 +74,13 @@ typedef enum {
 } NMIPConfigSource;
 
 /* platform */
+typedef struct _NMPlatform           NMPlatform;
 typedef struct _NMPlatformIP4Address NMPlatformIP4Address;
 typedef struct _NMPlatformIP4Route   NMPlatformIP4Route;
 typedef struct _NMPlatformIP6Address NMPlatformIP6Address;
 typedef struct _NMPlatformIP6Route   NMPlatformIP6Route;
 typedef struct _NMPlatformLink       NMPlatformLink;
-
+typedef struct _NMPNetns             NMPNetns;
 typedef struct _NMPObject            NMPObject;
 
 typedef enum {
diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf
index 801d9a7d..dd630e19 100644
--- a/src/org.freedesktop.NetworkManager.conf
+++ b/src/org.freedesktop.NetworkManager.conf
@@ -11,7 +11,7 @@
 
                 <allow send_interface="org.freedesktop.NetworkManager.SecretAgent"/>
                 <!-- These are there because some broken policies do
-		     <allow send_interface="..." /> (see dbus-daemon(8) for details).
+		     <deny send_interface="..." /> (see dbus-daemon(8) for details).
 		     This seems to override that for the known VPN plugins.
                   -->
                 <allow send_destination="org.freedesktop.NetworkManager.openconnect"/>
@@ -113,13 +113,20 @@
 		     to the agents themselves. -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.AgentManager"/>
-                <deny send_interface="org.freedesktop.NetworkManager.SecretAgent"/>
 
 		<!-- Root-only functions -->
-                <deny send_interface="org.freedesktop.NetworkManager" send_member="SetLogging"/>
-                <deny send_interface="org.freedesktop.NetworkManager" send_member="Sleep"/>
-                <deny send_interface="org.freedesktop.NetworkManager.Settings" send_member="LoadConnections"/>
-                <deny send_interface="org.freedesktop.NetworkManager.Settings" send_member="ReloadConnections"/>
+                <deny send_destination="org.freedesktop.NetworkManager"
+                      send_interface="org.freedesktop.NetworkManager"
+                      send_member="SetLogging"/>
+                <deny send_destination="org.freedesktop.NetworkManager"
+                      send_interface="org.freedesktop.NetworkManager"
+                      send_member="Sleep"/>
+                <deny send_destination="org.freedesktop.NetworkManager"
+                      send_interface="org.freedesktop.NetworkManager.Settings"
+                      send_member="LoadConnections"/>
+                <deny send_destination="org.freedesktop.NetworkManager"
+                      send_interface="org.freedesktop.NetworkManager.Settings"
+                      send_member="ReloadConnections"/>
         </policy>
 
 	<limit name="max_replies_per_connection">1024</limit>
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 68b72b19..fdb11eac 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -890,6 +890,7 @@ ip4_address_add (NMPlatform *platform,
                  in_addr_t peer_addr,
                  guint32 lifetime,
                  guint32 preferred,
+                 guint32 flags,
                  const char *label)
 {
 	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform);
@@ -905,6 +906,7 @@ ip4_address_add (NMPlatform *platform,
 	address.timestamp = nm_utils_get_monotonic_timestamp_s ();
 	address.lifetime = lifetime;
 	address.preferred = preferred;
+	address.n_ifa_flags = flags;
 	if (label)
 		g_strlcpy (address.label, label, sizeof (address.label));
 
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 196e75cb..e2ff1f61 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -50,6 +50,7 @@
 
 #include "nm-core-utils.h"
 #include "nmp-object.h"
+#include "nmp-netns.h"
 #include "nm-platform-utils.h"
 #include "wifi/wifi-utils.h"
 #include "wifi/wifi-utils-wext.h"
@@ -221,6 +222,7 @@ static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActi
 static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
 static void cache_prune_candidates_prune (NMPlatform *platform);
 static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks);
+static void _assert_netns_current (NMPlatform *platform);
 
 /*****************************************************************************/
 
@@ -647,6 +649,8 @@ _linktype_get_type (NMPlatform *platform,
 {
 	guint i;
 
+	_assert_netns_current (platform);
+
 	if (completed_from_cache) {
 		const NMPObject *obj;
 
@@ -1673,11 +1677,9 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only)
 
 	obj->ip_address.source = NM_IP_CONFIG_SOURCE_KERNEL;
 
-	if (!is_v4) {
-		obj->ip6_address.n_ifa_flags = tb[IFA_FLAGS]
-		                               ? nla_get_u32 (tb[IFA_FLAGS])
-		                               : ifa->ifa_flags;
-	}
+	obj->ip_address.n_ifa_flags = tb[IFA_FLAGS]
+	                              ? nla_get_u32 (tb[IFA_FLAGS])
+	                              : ifa->ifa_flags;
 
 	if (is_v4) {
 		if (tb[IFA_LABEL]) {
@@ -2225,7 +2227,7 @@ _nl_msg_new_address (int nlmsg_type,
 		NLA_PUT (msg, IFA_CACHEINFO, sizeof(ca), &ca);
 	}
 
-	if (flags & ~0xFF) {
+	if (flags & ~((guint32) 0xFF)) {
 		/* only set the IFA_FLAGS attribute, if they actually contain additional
 		 * flags that are not already set to am.ifa_flags.
 		 *
@@ -2412,6 +2414,7 @@ void
 nm_linux_platform_setup (void)
 {
 	g_object_new (NM_TYPE_LINUX_PLATFORM,
+	              NM_PLATFORM_NETNS_SUPPORT, FALSE,
 	              NM_PLATFORM_REGISTER_SINGLETON, TRUE,
 	              NULL);
 }
@@ -2419,6 +2422,16 @@ nm_linux_platform_setup (void)
 /******************************************************************/
 
 static void
+_assert_netns_current (NMPlatform *platform)
+{
+#if NM_MORE_ASSERTS
+	nm_assert (NM_IS_LINUX_PLATFORM (platform));
+
+	nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ()));
+#endif
+}
+
+static void
 _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value)
 {
 	GError *error = NULL;
@@ -2451,6 +2464,7 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *va
 static gboolean
 sysctl_set (NMPlatform *platform, const char *path, const char *value)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	int fd, tries;
 	gssize nwrote;
 	gsize len;
@@ -2466,6 +2480,9 @@ sysctl_set (NMPlatform *platform, const char *path, const char *value)
 	/* Don't write to suspicious locations */
 	g_assert (!strstr (path, "/../"));
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	fd = open (path, O_WRONLY | O_TRUNC);
 	if (fd == -1) {
 		if (errno == ENOENT) {
@@ -2580,6 +2597,7 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *co
 static char *
 sysctl_get (NMPlatform *platform, const char *path)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	GError *error = NULL;
 	char *contents;
 
@@ -2589,6 +2607,9 @@ sysctl_get (NMPlatform *platform, const char *path)
 	/* Don't write to suspicious locations */
 	g_assert (!strstr (path, "/../"));
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return NULL;
+
 	if (!g_file_get_contents (path, &contents, NULL, &error)) {
 		/* We assume FAILED means EOPNOTSUP */
 		if (   g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
@@ -2653,6 +2674,9 @@ do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cach
 	nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj));
 	nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj));
 
+	/* we raise the signals inside the namespace of the NMPlatform instance. */
+	_assert_netns_current (platform);
+
 	switch (cache_op) {
 	case NMP_CACHE_OPS_ADDED:
 		if (!nmp_object_is_visible (obj))
@@ -3857,6 +3881,7 @@ do_change_link (NMPlatform *platform,
                 int ifindex,
                 struct nl_msg *nlmsg)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
 	int nle;
 	char s_buf[256];
@@ -3864,6 +3889,9 @@ do_change_link (NMPlatform *platform,
 	NMLogLevel log_level = LOGL_DEBUG;
 	const char *log_result = "failure", *log_detail = "";
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return NM_PLATFORM_ERROR_UNSPECIFIED;
+
 retry:
 	nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
 	if (nle < 0) {
@@ -3927,7 +3955,7 @@ link_add (NMPlatform *platform,
 		 * bond0 automatically.
 		 */
 		if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS))
-			nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL);
+			(void) nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL);
 	}
 
 	_LOGD ("link: add link '%s' of type '%s' (%d)",
@@ -3994,7 +4022,7 @@ link_get_type_name (NMPlatform *platform, int ifindex)
 		return nm_link_type_to_string (obj->link.type);
 	}
 	/* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */
-	return str_if_set (obj->link.kind, "unknown");
+	return obj->link.kind ?: "unknown";
 }
 
 static gboolean
@@ -4023,6 +4051,31 @@ link_refresh (NMPlatform *platform, int ifindex)
 	return !!cache_lookup_link (platform, ifindex);
 }
 
+static gboolean
+link_set_netns (NMPlatform *platform,
+                int ifindex,
+                int netns_fd)
+{
+	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+
+	_LOGD ("link: move link %d to network namespace with fd %d", ifindex, netns_fd);
+
+	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
+	                          0,
+	                          ifindex,
+	                          NULL,
+	                          0,
+	                          0);
+	if (!nlmsg)
+		return FALSE;
+
+	NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd);
+	return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
+
+nla_put_failure:
+	g_return_val_if_reached (FALSE);
+}
+
 static NMPlatformError
 link_change_flags (NMPlatform *platform,
                    int ifindex,
@@ -4137,11 +4190,15 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
 static gboolean
 link_supports_carrier_detect (NMPlatform *platform, int ifindex)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	const char *name = nm_platform_link_get_name (platform, ifindex);
 
 	if (!name)
 		return FALSE;
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	/* We use netlink for the actual carrier detection, but netlink can't tell
 	 * us whether the device actually supports carrier detection in the first
 	 * place. We assume any device that does implements one of these two APIs.
@@ -4152,6 +4209,7 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex)
 static gboolean
 link_supports_vlans (NMPlatform *platform, int ifindex)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	const NMPObject *obj;
 
 	obj = cache_lookup_link (platform, ifindex);
@@ -4160,6 +4218,9 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
 	if (!obj || obj->link.arptype != ARPHRD_ETHER)
 		return FALSE;
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	return nmp_utils_ethtool_supports_vlans (obj->link.name);
 }
 
@@ -4198,6 +4259,11 @@ link_get_permanent_address (NMPlatform *platform,
                             guint8 *buf,
                             size_t *length)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length);
 }
 
@@ -4234,7 +4300,7 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex)
 	if (!ifname)
 		return NULL;
 
-	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+	ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname);
 
 	path = g_strdup_printf ("/sys/class/net/%s/phys_port_id", ifname);
 	id = sysctl_get (platform, path);
@@ -4254,7 +4320,7 @@ link_get_dev_id (NMPlatform *platform, int ifindex)
 	if (!ifname)
 		return 0;
 
-	ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+	ifname = NM_ASSERT_VALID_PATH_COMPONENT (ifname);
 
 	path = g_strdup_printf ("/sys/class/net/%s/dev_id", ifname);
 	id = sysctl_get (platform, path);
@@ -4837,7 +4903,7 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap,
 	if (fd < 0)
 		return FALSE;
 
-	strncpy (ifr.ifr_name, name, IFNAMSIZ);
+	nm_utils_ifname_cpy (ifr.ifr_name, name);
 	ifr.ifr_flags = tap ? IFF_TAP : IFF_TUN;
 
 	if (!pi)
@@ -4930,7 +4996,7 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMP
 
 	ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key);
 
-	path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name));
+	path = g_strdup_printf ("/sys/class/net/%s/create_child", NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name));
 	id = g_strdup_printf ("0x%04x", p_key);
 	if (!nm_platform_sysctl_set (platform, path, id))
 		return FALSE;
@@ -4977,15 +5043,19 @@ wifi_get_wifi_data (NMPlatform *platform, int ifindex)
 
 	return wifi_data;
 }
+#define WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, retval) \
+	nm_auto_pop_netns NMPNetns *netns = NULL; \
+	WifiData *wifi_data; \
+	if (!nm_platform_netns_push (platform, &netns)) \
+		return retval; \
+	wifi_data = wifi_get_wifi_data (platform, ifindex); \
+	if (!wifi_data) \
+		return retval;
 
 static gboolean
 wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	if (caps)
 		*caps = wifi_utils_get_caps (wifi_data);
 	return TRUE;
@@ -4994,90 +5064,64 @@ wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabiliti
 static gboolean
 wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_get_bssid (wifi_data, bssid);
 }
 
 static guint32
 wifi_get_frequency (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return 0;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0);
 	return wifi_utils_get_freq (wifi_data);
 }
 
 static gboolean
 wifi_get_quality (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_get_qual (wifi_data);
 }
 
 static guint32
 wifi_get_rate (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_get_rate (wifi_data);
 }
 
 static NM80211Mode
 wifi_get_mode (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return NM_802_11_MODE_UNKNOWN;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN);
 	return wifi_utils_get_mode (wifi_data);
 }
 
 static void
 wifi_set_mode (NMPlatform *platform, int ifindex, NM80211Mode mode)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (wifi_data)
-		wifi_utils_set_mode (wifi_data, mode);
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, );
+	wifi_utils_set_mode (wifi_data, mode);
 }
 
 static void
 wifi_set_powersave (NMPlatform *platform, int ifindex, guint32 powersave)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (wifi_data)
-		wifi_utils_set_powersave (wifi_data, powersave);
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, );
+	wifi_utils_set_powersave (wifi_data, powersave);
 }
 
 static guint32
 wifi_find_frequency (NMPlatform *platform, int ifindex, const guint32 *freqs)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return 0;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0);
 	return wifi_utils_find_freq (wifi_data, freqs);
 }
 
 static void
 wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean running)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (wifi_data)
-		wifi_utils_indicate_addressing_running (wifi_data, running);
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, );
+	wifi_utils_indicate_addressing_running (wifi_data, running);
 }
 
 /******************************************************************/
@@ -5085,33 +5129,21 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru
 static guint32
 mesh_get_channel (NMPlatform *platform, int ifindex)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return 0;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0);
 	return wifi_utils_get_mesh_channel (wifi_data);
 }
 
 static gboolean
 mesh_set_channel (NMPlatform *platform, int ifindex, guint32 channel)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_set_mesh_channel (wifi_data, channel);
 }
 
 static gboolean
 mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
 {
-	WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex);
-
-	if (!wifi_data)
-		return FALSE;
-
+	WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE);
 	return wifi_utils_set_mesh_ssid (wifi_data, ssid, len);
 }
 
@@ -5120,8 +5152,12 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len)
 static gboolean
 link_get_wake_on_lan (NMPlatform *platform, int ifindex)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLinkType type = nm_platform_link_get_type (platform, ifindex);
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	if (type == NM_LINK_TYPE_ETHERNET)
 		return nmp_utils_ethtool_get_wake_on_lan (nm_platform_link_get_name (platform, ifindex));
 	else if (type == NM_LINK_TYPE_WIFI) {
@@ -5142,6 +5178,11 @@ link_get_driver_info (NMPlatform *platform,
                       char **out_driver_version,
                       char **out_fw_version)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex),
 	                                          out_driver_name,
 	                                          out_driver_version,
@@ -5184,6 +5225,7 @@ ip4_address_add (NMPlatform *platform,
                  in_addr_t peer_addr,
                  guint32 lifetime,
                  guint32 preferred,
+                 guint32 flags,
                  const char *label)
 {
 	NMPObject obj_id;
@@ -5196,8 +5238,8 @@ ip4_address_add (NMPlatform *platform,
 	                             &addr,
 	                             plen,
 	                             &peer_addr,
-	                             0,
-	                             nmp_utils_ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE,
+	                             flags,
+	                             nm_utils_ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE,
 	                             lifetime,
 	                             preferred,
 	                             label);
@@ -5722,6 +5764,7 @@ out:
 static gboolean
 event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	int r, nle;
 	struct pollfd pfd;
@@ -5734,6 +5777,9 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
 		gint64 timeout_abs_ns;
 	} data_next;
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
 	while (TRUE) {
 
 		while (TRUE) {
@@ -5838,7 +5884,14 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_dev
 	NMPCacheOpsType cache_op;
 
 	cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform);
-	do_emit_signal (platform, obj_cache, cache_op, was_visible);
+
+	if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
+		nm_auto_pop_netns NMPNetns *netns = NULL;
+
+		if (!nm_platform_netns_push (platform, &netns))
+			return;
+		do_emit_signal (platform, obj_cache, cache_op, was_visible);
+	}
 }
 
 static void
@@ -5854,22 +5907,22 @@ udev_device_added (NMPlatform *platform,
 		return;
 	}
 
-	if (g_udev_device_get_property (udev_device, "IFINDEX"))
-		ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
-	else {
-		_LOGW ("(%s): udev-add: failed to get device's ifindex", ifname);
+	if (!g_udev_device_get_property (udev_device, "IFINDEX")) {
+		_LOGW ("udev-add[%s]failed to get device's ifindex", ifname);
 		return;
 	}
+	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
 	if (ifindex <= 0) {
-		_LOGW ("(%s): udev-add: retrieved invalid IFINDEX=%d", ifname, ifindex);
+		_LOGW ("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex);
 		return;
 	}
 
 	if (!g_udev_device_get_sysfs_path (udev_device)) {
-		_LOGD ("(%s): udev-add: couldn't determine device path; ignoring...", ifname);
+		_LOGD ("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex);
 		return;
 	}
 
+	_LOGT ("udev-add[%s,%d]: device added", ifname, ifindex);
 	cache_update_link_udev (platform, ifindex, udev_device);
 }
 
@@ -5909,6 +5962,7 @@ handle_udev_event (GUdevClient *client,
                    GUdevDevice *udev_device,
                    gpointer user_data)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMPlatform *platform = NM_PLATFORM (user_data);
 	const char *subsys;
 	const char *ifindex;
@@ -5916,6 +5970,9 @@ handle_udev_event (GUdevClient *client,
 
 	g_return_if_fail (action != NULL);
 
+	if (!nm_platform_netns_push (platform, &netns))
+		return;
+
 	/* A bit paranoid */
 	subsys = g_udev_device_get_subsystem (udev_device);
 	g_return_if_fail (!g_strcmp0 (subsys, "net"));
@@ -5938,15 +5995,22 @@ static void
 nm_linux_platform_init (NMLinuxPlatform *self)
 {
 	NMLinuxPlatformPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate);
+	gboolean use_udev;
+
+	use_udev =    nmp_netns_is_initial ()
+	           && access ("/sys", W_OK) == 0;
 
 	self->priv = priv;
 
 	priv->nlh_seq_next = 1;
-	priv->cache = nmp_cache_new ();
+	priv->cache = nmp_cache_new (use_udev);
 	priv->delayed_action.list_master_connected = g_ptr_array_new ();
 	priv->delayed_action.list_refresh_link = g_ptr_array_new ();
 	priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData));
 	priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
+
+	if (use_udev)
+		priv->udev_client = g_udev_client_new ((const char *[]) { "net", NULL });
 }
 
 static void
@@ -5954,14 +6018,22 @@ constructed (GObject *_object)
 {
 	NMPlatform *platform = NM_PLATFORM (_object);
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
-	const char *udev_subsys[] = { "net", NULL };
 	int channel_flags;
 	gboolean status;
 	int nle;
-	GUdevEnumerator *enumerator;
-	GList *devices, *iter;
 
-	_LOGD ("create");
+	nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ());
+
+	_LOGD ("create (%s netns, %s, %s udev)",
+	       !platform->_netns ? "ignore" : "use",
+	       !platform->_netns && nmp_netns_is_initial ()
+	           ? "initial netns"
+	           : (!nmp_netns_get_current ()
+	                ? "no netns support"
+	                : nm_sprintf_bufa (100, "in netns[%p]%s",
+	                                   nmp_netns_get_current (),
+	                                   nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")),
+	       nmp_cache_use_udev_get (priv->cache) ? "use" : "no");
 
 	priv->nlh = nl_socket_alloc ();
 	g_assert (priv->nlh);
@@ -5993,16 +6065,12 @@ constructed (GObject *_object)
 
 	channel_flags = g_io_channel_get_flags (priv->event_channel);
 	status = g_io_channel_set_flags (priv->event_channel,
-		channel_flags | G_IO_FLAG_NONBLOCK, NULL);
+	                                 channel_flags | G_IO_FLAG_NONBLOCK, NULL);
 	g_assert (status);
 	priv->event_id = g_io_add_watch (priv->event_channel,
 	                                (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
 	                                 event_handler, platform);
 
-	/* Set up udev monitoring */
-	priv->udev_client = g_udev_client_new (udev_subsys);
-	g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
-
 	/* complete construction of the GObject instance before populating the cache. */
 	G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object);
 
@@ -6017,19 +6085,27 @@ constructed (GObject *_object)
 
 	delayed_action_handle_all (platform, FALSE);
 
-	/* And read initial device list */
-	enumerator = g_udev_enumerator_new (priv->udev_client);
-	g_udev_enumerator_add_match_subsystem (enumerator, "net");
+	/* Set up udev monitoring */
+	if (priv->udev_client) {
+		GUdevEnumerator *enumerator;
+		GList *devices, *iter;
+
+		g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
+
+		/* And read initial device list */
+		enumerator = g_udev_enumerator_new (priv->udev_client);
+		g_udev_enumerator_add_match_subsystem (enumerator, "net");
 
-	g_udev_enumerator_add_match_is_initialized (enumerator);
+		g_udev_enumerator_add_match_is_initialized (enumerator);
 
-	devices = g_udev_enumerator_execute (enumerator);
-	for (iter = devices; iter; iter = g_list_next (iter)) {
-		udev_device_added (platform, G_UDEV_DEVICE (iter->data));
-		g_object_unref (G_UDEV_DEVICE (iter->data));
+		devices = g_udev_enumerator_execute (enumerator);
+		for (iter = devices; iter; iter = g_list_next (iter)) {
+			udev_device_added (platform, G_UDEV_DEVICE (iter->data));
+			g_object_unref (G_UDEV_DEVICE (iter->data));
+		}
+		g_list_free (devices);
+		g_object_unref (enumerator);
 	}
-	g_list_free (devices);
-	g_object_unref (enumerator);
 }
 
 static void
@@ -6048,6 +6124,11 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->prune_candidates, g_hash_table_unref);
 
+	if (priv->udev_client) {
+		g_signal_handlers_disconnect_by_func (priv->udev_client, G_CALLBACK (handle_udev_event), platform);
+		g_clear_object (&priv->udev_client);
+	}
+
 	G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object);
 }
 
@@ -6067,7 +6148,6 @@ nm_linux_platform_finalize (GObject *object)
 	g_io_channel_unref (priv->event_channel);
 	nl_socket_free (priv->nlh);
 
-	g_object_unref (priv->udev_client);
 	g_hash_table_unref (priv->wifi_data);
 
 	if (priv->sysctl_get_prev_values) {
@@ -6109,6 +6189,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 
 	platform_class->link_refresh = link_refresh;
 
+	platform_class->link_set_netns = link_set_netns;
+
 	platform_class->link_set_up = link_set_up;
 	platform_class->link_set_down = link_set_down;
 	platform_class->link_set_arp = link_set_arp;
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index 0f2656f2..d0c92a78 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -56,7 +56,7 @@ ethtool_get (const char *name, gpointer edata)
 	nm_assert (strlen (name) < IFNAMSIZ);
 
 	memset (&ifr, 0, sizeof (ifr));
-	strcpy (ifr.ifr_name, name);
+	nm_utils_ifname_cpy (ifr.ifr_name, name);
 	ifr.ifr_data = edata;
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
@@ -344,7 +344,7 @@ nmp_utils_mii_supports_carrier_detect (const char *ifname)
 	}
 
 	memset (&ifr, 0, sizeof (struct ifreq));
-	strncpy (ifr.ifr_name, ifname, IFNAMSIZ);
+	nm_utils_ifname_cpy (ifr.ifr_name, ifname);
 
 	errno = 0;
 	if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) {
@@ -412,114 +412,18 @@ out:
 	return g_intern_string (driver);
 }
 
-/******************************************************************
- * utils
- ******************************************************************/
-
-#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
-#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
-
-gboolean
-nmp_utils_ip4_address_is_link_local (in_addr_t addr)
-{
-	return (addr & IPV4LL_NETMASK) == IPV4LL_NETWORK;
-}
-
-/**
- * Takes a pair @timestamp and @duration, and returns the remaining duration based
- * on the new timestamp @now.
- */
-guint32
-nmp_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
-                                                guint32 duration,
-                                                guint32 now,
-                                                guint32 padding)
-{
-	gint64 t;
-
-	if (duration == NM_PLATFORM_LIFETIME_PERMANENT)
-		return NM_PLATFORM_LIFETIME_PERMANENT;
-
-	if (timestamp == 0) {
-		/* if the @timestamp is zero, assume it was just left unset and that the relative
-		 * @duration starts counting from @now. This is convenient to construct an address
-		 * and print it in nm_platform_ip4_address_to_string().
-		 *
-		 * In general it does not make sense to set the @duration without anchoring at
-		 * @timestamp because you don't know the absolute expiration time when looking
-		 * at the address at a later moment. */
-		timestamp = now;
-	}
-
-	/* For timestamp > now, just accept it and calculate the expected(?) result. */
-	t = (gint64) timestamp + (gint64) duration - (gint64) now;
-
-	/* Optional padding to avoid potential races. */
-	t += (gint64) padding;
-
-	if (t <= 0)
-		return 0;
-	if (t >= NM_PLATFORM_LIFETIME_PERMANENT)
-		return NM_PLATFORM_LIFETIME_PERMANENT - 1;
-	return t;
-}
-
-gboolean
-nmp_utils_lifetime_get (guint32 timestamp,
-                        guint32 lifetime,
-                        guint32 preferred,
-                        guint32 now,
-                        guint32 padding,
-                        guint32 *out_lifetime,
-                        guint32 *out_preferred)
-{
-	guint32 t_lifetime, t_preferred;
-
-	if (lifetime == 0) {
-		*out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
-		*out_preferred = NM_PLATFORM_LIFETIME_PERMANENT;
-
-		/* We treat lifetime==0 as permanent addresses to allow easy creation of such addresses
-		 * (without requiring to set the lifetime fields to NM_PLATFORM_LIFETIME_PERMANENT).
-		 * In that case we also expect that the other fields (timestamp and preferred) are left unset. */
-		g_return_val_if_fail (timestamp == 0 && preferred == 0, TRUE);
-	} else {
-		if (!now)
-			now = nm_utils_get_monotonic_timestamp_s ();
-		t_lifetime = nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now, padding);
-		if (!t_lifetime) {
-			*out_lifetime = 0;
-			*out_preferred = 0;
-			return FALSE;
-		}
-		t_preferred = nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now, padding);
-
-		*out_lifetime = t_lifetime;
-		*out_preferred = MIN (t_preferred, t_lifetime);
-
-		/* Assert that non-permanent addresses have a (positive) @timestamp. nmp_utils_lifetime_rebase_relative_time_on_now()
-		 * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
-		 * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
-		 */
-		g_return_val_if_fail (   timestamp != 0
-		                      || (   lifetime  == NM_PLATFORM_LIFETIME_PERMANENT
-		                          && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE);
-		g_return_val_if_fail (t_preferred <= t_lifetime, TRUE);
-	}
-	return TRUE;
-}
-
 gboolean
 nmp_utils_device_exists (const char *name)
 {
 #define SYS_CLASS_NET "/sys/class/net/"
-	char sysdir[NM_STRLEN (SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET;
+	char sysdir[NM_STRLEN (SYS_CLASS_NET) + IFNAMSIZ];
 
 	if (   !name
 	    || strlen (name) >= IFNAMSIZ
 	    || !nm_utils_is_valid_path_component (name))
 		g_return_val_if_reached (FALSE);
 
-	strcpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], name);
+	memcpy (sysdir, SYS_CLASS_NET, NM_STRLEN (SYS_CLASS_NET));
+	nm_utils_ifname_cpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], name);
 	return g_file_test (sysdir, G_FILE_TEST_EXISTS);
 }
diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h
index a9d29c04..976bd8db 100644
--- a/src/platform/nm-platform-utils.h
+++ b/src/platform/nm-platform-utils.h
@@ -52,21 +52,6 @@ gboolean nmp_utils_mii_supports_carrier_detect (const char *ifname);
 
 const char *nmp_utils_udev_get_driver (GUdevDevice *device);
 
-guint32 nmp_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
-                                                        guint32 duration,
-                                                        guint32 now,
-                                                        guint32 padding);
-
-gboolean nmp_utils_lifetime_get (guint32 timestamp,
-                                 guint32 lifetime,
-                                 guint32 preferred,
-                                 guint32 now,
-                                 guint32 padding,
-                                 guint32 *out_lifetime,
-                                 guint32 *out_preferred);
-
 gboolean nmp_utils_device_exists (const char *name);
 
-gboolean nmp_utils_ip4_address_is_link_local (in_addr_t addr);
-
 #endif /* __NM_PLATFORM_UTILS_H__ */
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index e3126442..d5ee48d4 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -41,6 +41,7 @@
 #include "nm-enum-types.h"
 #include "nm-platform-utils.h"
 #include "nmp-object.h"
+#include "nmp-netns.h"
 
 /*****************************************************************************/
 
@@ -48,8 +49,6 @@ const NMIPAddr nm_ip_addr_zero = NMIPAddrInit;
 
 /*****************************************************************************/
 
-#define ADDRESS_LIFETIME_PADDING 5
-
 G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX);
 G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address));
 G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address));
@@ -89,6 +88,7 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 };
 
 enum {
 	PROP_0,
+	PROP_NETNS_SUPPORT,
 	PROP_REGISTER_SINGLETON,
 	LAST_PROP,
 };
@@ -666,6 +666,32 @@ nm_platform_link_delete (NMPlatform *self, int ifindex)
 }
 
 /**
+ * nm_platform_link_set_netns:
+ * @self: platform instance
+ * @ifindex: Interface index
+ * @netns_fd: the file descriptor for the new netns.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd)
+{
+	const NMPlatformLink *pllink;
+
+	_CHECK_SELF (self, klass, FALSE);
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+	g_return_val_if_fail (netns_fd > 0, FALSE);
+
+	pllink = nm_platform_link_get (self, ifindex);
+	if (!pllink)
+		return FALSE;
+
+	_LOGD ("link: ifindex %d changing network namespace to %d", ifindex, netns_fd);
+	return klass->link_set_netns (self, ifindex, netns_fd);
+}
+
+/**
  * nm_platform_link_get_index:
  * @self: platform instance
  * @name: Interface name
@@ -1625,9 +1651,9 @@ link_option_path (NMPlatform *self, int master, const char *category, const char
 		return NULL;
 
 	return g_strdup_printf ("/sys/class/net/%s/%s/%s",
-	                        ASSERT_VALID_PATH_COMPONENT (name),
-	                        ASSERT_VALID_PATH_COMPONENT (category),
-	                        ASSERT_VALID_PATH_COMPONENT (option));
+	                        NM_ASSERT_VALID_PATH_COMPONENT (name),
+	                        NM_ASSERT_VALID_PATH_COMPONENT (category),
+	                        NM_ASSERT_VALID_PATH_COMPONENT (option));
 }
 
 static gboolean
@@ -1916,7 +1942,7 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self,
 	/* Could not get the link information via netlink. To support older kernels,
 	 * fallback to reading sysfs. */
 
-	iface = ASSERT_VALID_PATH_COMPONENT (plink->name);
+	iface = NM_ASSERT_VALID_PATH_COMPONENT (plink->name);
 
 	/* Fall back to reading sysfs */
 	path = g_strdup_printf ("/sys/class/net/%s/mode", iface);
@@ -2128,6 +2154,10 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe
 
 	/* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */
 	if (out_peer_ifindex) {
+		nm_auto_pop_netns NMPNetns *netns = NULL;
+
+		if (!nm_platform_netns_push (self, &netns))
+			return FALSE;
 		peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->name);
 		if (peer_ifindex <= 0)
 			return FALSE;
@@ -2368,6 +2398,52 @@ _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size)
 	return buf;
 }
 
+#define TO_STRING_IFA_FLAGS_BUF_SIZE 256
+
+static const char *
+_to_string_ifa_flags (guint32 ifa_flags, char *buf, gsize size)
+{
+#define S_FLAGS_PREFIX " flags "
+	nm_assert (buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN (S_FLAGS_PREFIX));
+
+	if (!ifa_flags)
+		buf[0] = '\0';
+	else {
+		nm_platform_addr_flags2str (ifa_flags, &buf[NM_STRLEN (S_FLAGS_PREFIX)], size - NM_STRLEN (S_FLAGS_PREFIX));
+		if (buf[NM_STRLEN (S_FLAGS_PREFIX)] == '\0')
+			buf[0] = '\0';
+		else
+			memcpy (buf, S_FLAGS_PREFIX, NM_STRLEN (S_FLAGS_PREFIX));
+	}
+	return buf;
+}
+
+/******************************************************************/
+
+gboolean
+nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, const char *ifname, NMSettingWiredWakeOnLan wol, const char *wol_password)
+{
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+	_CHECK_SELF (self, klass, FALSE);
+
+	if (!nm_platform_netns_push (self, &netns))
+		return FALSE;
+
+	return nmp_utils_ethtool_set_wake_on_lan (ifname, wol, wol_password);
+}
+
+gboolean
+nm_platform_ethtool_get_link_speed (NMPlatform *self, const char *ifname, guint32 *out_speed)
+{
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+	_CHECK_SELF (self, klass, FALSE);
+
+	if (!nm_platform_netns_push (self, &netns))
+		return FALSE;
+
+	return nmp_utils_ethtool_get_link_speed (ifname, out_speed);
+}
+
 /******************************************************************/
 
 void
@@ -2415,6 +2491,7 @@ nm_platform_ip4_address_add (NMPlatform *self,
                              in_addr_t peer_address,
                              guint32 lifetime,
                              guint32 preferred,
+                             guint32 flags,
                              const char *label)
 {
 	_CHECK_SELF (self, klass, FALSE);
@@ -2435,12 +2512,13 @@ nm_platform_ip4_address_add (NMPlatform *self,
 		addr.timestamp = 0; /* set it at zero, which to_string will treat as *now* */
 		addr.lifetime = lifetime;
 		addr.preferred = preferred;
+		addr.n_ifa_flags = flags;
 		if (label)
 			g_strlcpy (addr.label, label, sizeof (addr.label));
 
 		_LOGD ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr, NULL, 0));
 	}
-	return klass->ip4_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, label);
+	return klass->ip4_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, flags, label);
 }
 
 gboolean
@@ -2535,7 +2613,7 @@ nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr addr
 }
 
 static gboolean
-array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address, gint64 now, guint32 padding)
+array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address, gint32 now)
 {
 	guint len = addresses ? addresses->len : 0;
 	guint i;
@@ -2548,8 +2626,8 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address
 		    && ((candidate->peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) {
 			guint32 lifetime, preferred;
 
-			if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
-			                            now, padding, &lifetime, &preferred))
+			if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
+			                           now, &lifetime, &preferred))
 				return TRUE;
 		}
 	}
@@ -2558,7 +2636,7 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address
 }
 
 static gboolean
-array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address, gint64 now, guint32 padding)
+array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address, gint32 now)
 {
 	guint len = addresses ? addresses->len : 0;
 	guint i;
@@ -2569,8 +2647,8 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address
 		if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) {
 			guint32 lifetime, preferred;
 
-			if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
-			                            now, padding, &lifetime, &preferred))
+			if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
+			                           now, &lifetime, &preferred))
 				return TRUE;
 		}
 	}
@@ -2599,7 +2677,7 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known
 {
 	GArray *addresses;
 	NMPlatformIP4Address *address;
-	guint32 now = nm_utils_get_monotonic_timestamp_s ();
+	gint32 now = nm_utils_get_monotonic_timestamp_s ();
 	int i;
 
 	_CHECK_SELF (self, klass, FALSE);
@@ -2609,7 +2687,7 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known
 	for (i = 0; i < addresses->len; i++) {
 		address = &g_array_index (addresses, NMPlatformIP4Address, i);
 
-		if (!array_contains_ip4_address (known_addresses, address, now, ADDRESS_LIFETIME_PADDING))
+		if (!array_contains_ip4_address (known_addresses, address, now))
 			nm_platform_ip4_address_delete (self, ifindex, address->address, address->plen, address->peer_address);
 	}
 	g_array_free (addresses, TRUE);
@@ -2625,11 +2703,13 @@ nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known
 		const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i);
 		guint32 lifetime, preferred;
 
-		if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
-		                             now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred))
+		if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+		                            now, &lifetime, &preferred))
 			continue;
 
-		if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, lifetime, preferred, known_address->label))
+		if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen,
+		                                  known_address->peer_address, lifetime, preferred,
+		                                  0, known_address->label))
 			return FALSE;
 
 		if (out_added_addresses) {
@@ -2660,7 +2740,7 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known
 {
 	GArray *addresses;
 	NMPlatformIP6Address *address;
-	guint32 now = nm_utils_get_monotonic_timestamp_s ();
+	gint32 now = nm_utils_get_monotonic_timestamp_s ();
 	int i;
 
 	/* Delete unknown addresses */
@@ -2672,7 +2752,7 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known
 		if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address))
 			continue;
 
-		if (!array_contains_ip6_address (known_addresses, address, now, ADDRESS_LIFETIME_PADDING))
+		if (!array_contains_ip6_address (known_addresses, address, now))
 			nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen);
 	}
 	g_array_free (addresses, TRUE);
@@ -2685,8 +2765,8 @@ nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known
 		const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i);
 		guint32 lifetime, preferred;
 
-		if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
-		                             now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred))
+		if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
+		                            now, &lifetime, &preferred))
 			continue;
 
 		if (!nm_platform_ip6_address_add (self, ifindex, known_address->address,
@@ -2930,7 +3010,7 @@ _lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf,
 		return "forever";
 
 	g_snprintf (buf, buf_size, "%usec",
-	            nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now, 0));
+	            nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now));
 	return buf;
 }
 
@@ -3025,7 +3105,7 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
 	            str_flags->str,
 	            link->mtu, master,
 	            link->arptype,
-	            str_if_set (str_link_type, "???"),
+	            str_link_type ?: "???",
 	            link->kind ? (g_strcmp0 (str_link_type, link->kind) ? "/" : "*") : "?",
 	            link->kind && g_strcmp0 (str_link_type, link->kind) ? link->kind : "",
 	            link->initialized ? " init" : " not-init",
@@ -3346,6 +3426,7 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize
 const char *
 nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len)
 {
+	char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
 	char s_address[INET_ADDRSTRLEN];
 	char s_peer[INET_ADDRSTRLEN];
 	char str_dev[TO_STRING_DEV_BUF_SIZE];
@@ -3383,10 +3464,11 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *bu
 	str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
 
 	g_snprintf (buf, len,
-	            "%s/%d lft %s pref %s%s%s%s%s src %s",
+	            "%s/%d lft %s pref %s%s%s%s%s%s src %s",
 	            s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
 	            str_peer ? str_peer : "",
 	            str_dev,
+	            _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
 	            str_label,
 	            source_to_string (address->source));
 	g_free (str_peer);
@@ -3457,8 +3539,7 @@ NM_UTILS_ENUM2STR_DEFINE (nm_platform_route_scope2str, int,
 const char *
 nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len)
 {
-#define S_FLAGS_PREFIX " flags "
-	char s_flags[256];
+	char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
 	char s_address[INET6_ADDRSTRLEN];
 	char s_peer[INET6_ADDRSTRLEN];
 	char str_lft[30], str_pref[30], str_time[50];
@@ -3479,12 +3560,6 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu
 
 	_to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev));
 
-	nm_platform_addr_flags2str (address->n_ifa_flags, &s_flags[NM_STRLEN (S_FLAGS_PREFIX)], sizeof (s_flags) - NM_STRLEN (S_FLAGS_PREFIX));
-	if (s_flags[NM_STRLEN (S_FLAGS_PREFIX)] == '\0')
-		s_flags[0] = '\0';
-	else
-		memcpy (s_flags, S_FLAGS_PREFIX, NM_STRLEN (S_FLAGS_PREFIX));
-
 	str_lft_p = _lifetime_to_string (address->timestamp,
 	                                 address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT,
 	                                 now, str_lft, sizeof (str_lft)),
@@ -3500,7 +3575,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu
 	            s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
 	            str_peer ? str_peer : "",
 	            str_dev,
-	            s_flags,
+	            _to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
 	            source_to_string (address->source));
 	g_free (str_peer);
 	return buf;
@@ -3825,6 +3900,7 @@ nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4A
 	_CMP_FIELD (a, b, timestamp);
 	_CMP_FIELD (a, b, lifetime);
 	_CMP_FIELD (a, b, preferred);
+	_CMP_FIELD (a, b, n_ifa_flags);
 	_CMP_FIELD_STR (a, b, label);
 	return 0;
 }
@@ -3980,6 +4056,31 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform
 
 /******************************************************************/
 
+NMPNetns *
+nm_platform_netns_get (NMPlatform *self)
+{
+	_CHECK_SELF (self, klass, NULL);
+
+	return self->_netns;
+}
+
+gboolean
+nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns)
+{
+	g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE);
+
+	if (   platform->_netns
+	    && !nmp_netns_push (platform->_netns)) {
+		NM_SET_OUT (netns, NULL);
+		return FALSE;
+	}
+
+	NM_SET_OUT (netns, platform->_netns);
+	return TRUE;
+}
+
+/******************************************************************/
+
 static gboolean
 _vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric)
 {
@@ -4079,9 +4180,20 @@ static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
 {
-	NMPlatformPrivate *priv =  NM_PLATFORM_GET_PRIVATE (object);
+	NMPlatform *self = NM_PLATFORM (object);
+	NMPlatformPrivate *priv =  NM_PLATFORM_GET_PRIVATE (self);
 
 	switch (prop_id) {
+	case PROP_NETNS_SUPPORT:
+		/* construct-only */
+		if (g_value_get_boolean (value)) {
+			NMPNetns *netns;
+
+			netns = nmp_netns_get_current ();
+			if (netns)
+				self->_netns = g_object_ref (netns);
+		}
+		break;
 	case PROP_REGISTER_SINGLETON:
 		/* construct-only */
 		priv->register_singleton = g_value_get_boolean (value);
@@ -4110,6 +4222,14 @@ nm_platform_init (NMPlatform *object)
 }
 
 static void
+finalize (GObject *object)
+{
+	NMPlatform *self = NM_PLATFORM (object);
+
+	g_clear_object (&self->_netns);
+}
+
+static void
 nm_platform_class_init (NMPlatformClass *platform_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (platform_class);
@@ -4118,10 +4238,19 @@ nm_platform_class_init (NMPlatformClass *platform_class)
 
 	object_class->set_property = set_property;
 	object_class->constructed = constructed;
+	object_class->finalize = finalize;
 
 	platform_class->wifi_set_powersave = wifi_set_powersave;
 
 	g_object_class_install_property
+	 (object_class, PROP_NETNS_SUPPORT,
+	     g_param_spec_boolean (NM_PLATFORM_NETNS_SUPPORT, "", "",
+	                           FALSE,
+	                           G_PARAM_WRITABLE |
+	                           G_PARAM_CONSTRUCT_ONLY |
+	                           G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property
 	 (object_class, PROP_REGISTER_SINGLETON,
 	     g_param_spec_boolean (NM_PLATFORM_REGISTER_SINGLETON, "", "",
 	                           FALSE,
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index b94c440a..8c97f766 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -31,6 +31,7 @@
 
 #include "nm-core-utils.h"
 #include "nm-setting-vlan.h"
+#include "nm-setting-wired.h"
 
 #define NM_TYPE_PLATFORM            (nm_platform_get_type ())
 #define NM_PLATFORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform))
@@ -41,6 +42,7 @@
 
 /******************************************************************/
 
+#define NM_PLATFORM_NETNS_SUPPORT      "netns-support"
 #define NM_PLATFORM_REGISTER_SINGLETON "register-singleton"
 
 /******************************************************************/
@@ -177,8 +179,6 @@ typedef enum {
 	NM_PLATFORM_SIGNAL_REMOVED,
 } NMPlatformSignalChangeType;
 
-#define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32
-
 typedef enum { /*< skip >*/
 	NM_PLATFORM_GET_ROUTE_FLAGS_NONE                            = 0,
 
@@ -221,6 +221,11 @@ typedef struct {
 	guint32 timestamp; \
 	guint32 lifetime;   /* seconds since timestamp */ \
 	guint32 preferred;  /* seconds since timestamp */ \
+	\
+	/* ifa_flags in 'struct ifaddrmsg' from <linux/if_addr.h>, extended to 32 bit by
+	 * IFA_FLAGS attribute. */ \
+	guint32 n_ifa_flags; \
+	\
 	int plen; \
 	;
 
@@ -269,7 +274,6 @@ struct _NMPlatformIP6Address {
 	__NMPlatformIPAddress_COMMON;
 	struct in6_addr address;
 	struct in6_addr peer_address;
-	guint32 n_ifa_flags; /* ifa_flags from <linux/if_addr.h>, field type "unsigned int" is as used in rtnl_addr_get_flags. */
 };
 
 typedef union {
@@ -459,6 +463,8 @@ typedef struct {
 
 struct _NMPlatform {
 	GObject parent;
+
+	NMPNetns *_netns;
 };
 
 typedef struct {
@@ -485,6 +491,9 @@ typedef struct {
 	gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged);
 
 	gboolean (*link_refresh) (NMPlatform *, int ifindex);
+
+	gboolean (*link_set_netns) (NMPlatform *, int ifindex, int netns_fd);
+
 	void (*process_events) (NMPlatform *self);
 
 	gboolean (*link_set_up) (NMPlatform *, int ifindex, gboolean *out_no_firmware);
@@ -586,6 +595,7 @@ typedef struct {
 	                             in_addr_t peer_address,
 	                             guint32 lifetime,
 	                             guint32 preferred_lft,
+	                             guint32 flags,
 	                             const char *label);
 	gboolean (*ip6_address_add) (NMPlatform *,
 	                             int ifindex,
@@ -665,6 +675,9 @@ _nm_platform_uint8_inv (guint8 scope)
 	return (guint8) ~scope;
 }
 
+NMPNetns *nm_platform_netns_get (NMPlatform *self);
+gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns);
+
 const char *nm_link_type_to_string (NMLinkType link_type);
 
 const char *_nm_platform_error_to_string (NMPlatformError error);
@@ -688,6 +701,8 @@ NMPlatformError nm_platform_link_bond_add (NMPlatform *self, const char *name, c
 NMPlatformError nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link);
 gboolean nm_platform_link_delete (NMPlatform *self, int ifindex);
 
+gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd);
+
 /* convienience methods to lookup the link and access fields of NMPlatformLink. */
 int nm_platform_link_get_ifindex (NMPlatform *self, const char *name);
 const char *nm_platform_link_get_name (NMPlatform *self, int ifindex);
@@ -853,6 +868,7 @@ gboolean nm_platform_ip4_address_add (NMPlatform *self,
                                       in_addr_t peer_address,
                                       guint32 lifetime,
                                       guint32 preferred_lft,
+                                      guint32 flags,
                                       const char *label);
 gboolean nm_platform_ip6_address_add (NMPlatform *self,
                                       int ifindex,
@@ -925,4 +941,7 @@ const char *nm_platform_route_scope2str (int scope, char *buf, gsize len);
 
 int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b);
 
+gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, const char *ifname, NMSettingWiredWakeOnLan wol, const char *wol_password);
+gboolean nm_platform_ethtool_get_link_speed (NMPlatform *self, const char *ifname, guint32 *out_speed);
+
 #endif /* __NETWORKMANAGER_PLATFORM_H__ */
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
new file mode 100644
index 00000000..26295855
--- /dev/null
+++ b/src/platform/nmp-netns.c
@@ -0,0 +1,732 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+#include "nmp-netns.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "NetworkManagerUtils.h"
+
+#define PROC_SELF_NS_MNT "/proc/self/ns/mnt"
+#define PROC_SELF_NS_NET "/proc/self/ns/net"
+
+#define _CLONE_NS_ALL    ((int) (CLONE_NEWNS | CLONE_NEWNET))
+#define _CLONE_NS_ALL_V          CLONE_NEWNS , CLONE_NEWNET
+
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_clone_ns_to_str, int,
+	NM_UTILS_FLAGS2STR (CLONE_NEWNS,  "mnt"),
+	NM_UTILS_FLAGS2STR (CLONE_NEWNET, "net"),
+);
+
+static const char *
+__ns_types_to_str (int ns_types, int ns_types_already_set, char *buf, gsize len)
+{
+	const char *b = buf;
+	char bb[200];
+
+	nm_utils_strbuf_append_c (&buf, &len, '[');
+	if (ns_types & ~ns_types_already_set) {
+		nm_utils_strbuf_append_str (&buf, &len,
+		                            _clone_ns_to_str (ns_types & ~ns_types_already_set, bb, sizeof (bb)));
+	}
+	if (ns_types & ns_types_already_set) {
+		if (ns_types & ~ns_types_already_set)
+			nm_utils_strbuf_append_c (&buf, &len, '/');
+		nm_utils_strbuf_append_str (&buf, &len,
+		                            _clone_ns_to_str (ns_types & ns_types_already_set, bb, sizeof (bb)));
+	}
+	nm_utils_strbuf_append_c (&buf, &len, ']');
+	return b;
+}
+#define _ns_types_to_str(ns_types, ns_types_already_set, buf) \
+	__ns_types_to_str (ns_types, ns_types_already_set, buf, sizeof (buf))
+
+/*********************************************************************************************/
+
+#define _NMLOG_DOMAIN        LOGD_PLATFORM
+#define _NMLOG_PREFIX_NAME   "netns"
+#define _NMLOG(level, netns, ...) \
+    G_STMT_START { \
+        NMLogLevel _level = (level); \
+        \
+        if (nm_logging_enabled (_level, _NMLOG_DOMAIN)) { \
+            NMPNetns *_netns = (netns); \
+            char _sbuf[20]; \
+            \
+            _nm_log (_level, _NMLOG_DOMAIN, 0, \
+                     "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                     _NMLOG_PREFIX_NAME, \
+                     (_netns ? nm_sprintf_buf (_sbuf, "[%p]", _netns) : "") \
+                     _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*********************************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_FD_NET,
+	PROP_FD_MNT,
+);
+
+typedef struct _NMPNetnsPrivate NMPNetnsPrivate;
+
+struct _NMPNetnsPrivate {
+	int fd_net;
+	int fd_mnt;
+};
+
+typedef struct {
+	NMPNetns *netns;
+	int count;
+	int ns_types;
+} NetnsInfo;
+
+static void _stack_push (NMPNetns *netns, int ns_types);
+static NMPNetns *_netns_new (GError **error);
+
+/*********************************************************************************************/
+
+static GArray *netns_stack = NULL;
+
+static void
+_stack_ensure_init_impl (void)
+{
+	NMPNetns *netns;
+	GError *error = NULL;
+
+	nm_assert (!netns_stack);
+
+	netns_stack = g_array_new (FALSE, FALSE, sizeof (NetnsInfo));
+
+	/* at the bottom of the stack we must try to create a netns instance
+	 * that we never pop. It's the base to which we need to return. */
+
+	netns = _netns_new (&error);
+
+	if (!netns) {
+		/* don't know how to recover from this error. Netns are not supported. */
+		_LOGE (NULL, "failed to create initial netns: %s", error->message);
+		g_clear_error (&error);
+		return;
+	}
+
+	_stack_push (netns, _CLONE_NS_ALL);
+
+	/* we leak this instance inside netns_stack. It cannot be popped. */
+	g_object_unref (netns);
+}
+#define _stack_ensure_init() \
+	G_STMT_START { \
+		if (G_UNLIKELY (!netns_stack)) { \
+			_stack_ensure_init_impl (); \
+		} \
+	} G_STMT_END
+
+static NMPNetns *
+_stack_current_netns (int ns_types)
+{
+	guint j;
+
+	nm_assert (netns_stack && netns_stack->len > 0);
+
+	/* we search the stack top-down to find the netns that has
+	 * all @ns_types set. */
+	for (j = netns_stack->len; ns_types && j >= 1; ) {
+		NetnsInfo *info;
+
+		info = &g_array_index (netns_stack, NetnsInfo, --j);
+
+		if (NM_FLAGS_ALL (info->ns_types, ns_types))
+			return info->netns;
+	}
+
+	g_return_val_if_reached (NULL);
+}
+
+static int
+_stack_current_ns_types (NMPNetns *netns, int ns_types)
+{
+	const int ns_types_check[] = { _CLONE_NS_ALL_V };
+	guint i, j;
+	int res = 0;
+
+	nm_assert (netns);
+	nm_assert (netns_stack && netns_stack->len > 0);
+
+	/* we search the stack top-down to check which of @ns_types
+	 * are already set to @netns. */
+	for (j = netns_stack->len; ns_types && j >= 1; ) {
+		NetnsInfo *info;
+
+		info = &g_array_index (netns_stack, NetnsInfo, --j);
+		if (info->netns != netns) {
+			ns_types = NM_FLAGS_UNSET (ns_types, info->ns_types);
+			continue;
+		}
+
+		for (i = 0; i < G_N_ELEMENTS (ns_types_check); i++) {
+			if (   NM_FLAGS_HAS (ns_types, ns_types_check[i])
+			    && NM_FLAGS_HAS (info->ns_types, ns_types_check[i])) {
+				res = NM_FLAGS_SET (res, ns_types_check[i]);
+				ns_types = NM_FLAGS_UNSET (ns_types, ns_types_check[i]);
+			}
+		}
+	}
+
+	return res;
+}
+
+static NetnsInfo *
+_stack_peek (void)
+{
+	nm_assert (netns_stack);
+
+	if (netns_stack->len > 0)
+		return &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
+	return NULL;
+}
+
+static NetnsInfo *
+_stack_bottom (void)
+{
+	nm_assert (netns_stack);
+
+	if (netns_stack->len > 0)
+		return &g_array_index (netns_stack, NetnsInfo, 0);
+	return NULL;
+}
+
+static void
+_stack_push (NMPNetns *netns, int ns_types)
+{
+	NetnsInfo *info;
+
+	nm_assert (netns_stack);
+	nm_assert (NMP_IS_NETNS (netns));
+	nm_assert (NM_FLAGS_ANY (ns_types, _CLONE_NS_ALL));
+	nm_assert (!NM_FLAGS_ANY (ns_types, ~_CLONE_NS_ALL));
+
+	g_array_set_size (netns_stack, netns_stack->len + 1);
+
+	info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
+	info->netns = g_object_ref (netns);
+	info->ns_types = ns_types;
+	info->count = 1;
+}
+
+static void
+_stack_pop (void)
+{
+	NetnsInfo *info;
+
+	nm_assert (netns_stack);
+	nm_assert (netns_stack->len > 1);
+
+	info = &g_array_index (netns_stack, NetnsInfo, (netns_stack->len - 1));
+
+	nm_assert (NMP_IS_NETNS (info->netns));
+	nm_assert (info->count == 1);
+
+	g_object_unref (info->netns);
+
+	g_array_set_size (netns_stack, netns_stack->len - 1);
+}
+
+static guint
+_stack_size (void)
+{
+	nm_assert (netns_stack);
+
+	return netns_stack->len;
+}
+
+/*********************************************************************************************/
+
+G_DEFINE_TYPE (NMPNetns, nmp_netns, G_TYPE_OBJECT);
+
+#define NMP_NETNS_GET_PRIVATE(o) ((o)->priv)
+
+/*********************************************************************************************/
+
+static NMPNetns *
+_netns_new (GError **error)
+{
+	NMPNetns *self;
+	int fd_net, fd_mnt;
+	int errsv;
+
+	fd_net = open (PROC_SELF_NS_NET, O_RDONLY);
+	if (fd_net == -1) {
+		errsv = errno;
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "Failed opening netns: %s",
+		             g_strerror (errsv));
+		return NULL;
+	}
+
+	fd_mnt = open (PROC_SELF_NS_MNT, O_RDONLY);
+	if (fd_mnt == -1) {
+		errsv = errno;
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "Failed opening mntns: %s",
+		             g_strerror (errsv));
+		close (fd_net);
+		return NULL;
+	}
+
+	self = g_object_new (NMP_TYPE_NETNS,
+	                     NMP_NETNS_FD_NET, fd_net,
+	                     NMP_NETNS_FD_MNT, fd_mnt,
+	                     NULL);
+
+	_LOGD (self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt);
+
+	return self;
+}
+
+static int
+_setns (NMPNetns *self, int type)
+{
+	char buf[100];
+	int fd;
+
+	nm_assert (NM_IN_SET (type, _CLONE_NS_ALL_V));
+
+	fd = (type == CLONE_NEWNET) ? self->priv->fd_net : self->priv->fd_mnt;
+
+	_LOGt (self, "set netns(%s, %d)", _ns_types_to_str (type, 0, buf), fd);
+
+	return setns (fd, type);
+}
+
+static gboolean
+_netns_switch_push (NMPNetns *self, int ns_types)
+{
+	int errsv;
+
+	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
+	    && !_stack_current_ns_types (self, CLONE_NEWNET)
+	    && _setns (self, CLONE_NEWNET) != 0) {
+		errsv = errno;
+		_LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
+		return FALSE;
+	}
+	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
+	    && !_stack_current_ns_types (self, CLONE_NEWNS)
+	    && _setns (self, CLONE_NEWNS) != 0) {
+		errsv = errno;
+		_LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
+
+		/* try to fix the mess by returning to the previous netns. */
+		if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
+	        && !_stack_current_ns_types (self, CLONE_NEWNET)) {
+			self = _stack_current_netns (CLONE_NEWNET);
+			if (   self
+			    && _setns (self, CLONE_NEWNET) != 0) {
+				errsv = errno;
+				_LOGE (self, "failed to restore netns: %s", g_strerror (errsv));
+			}
+		}
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+_netns_switch_pop (NMPNetns *self, int ns_types)
+{
+	int errsv;
+	NMPNetns *current;
+	int success = TRUE;
+
+	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNET)
+	    && (!self || !_stack_current_ns_types (self, CLONE_NEWNET))) {
+		current = _stack_current_netns (CLONE_NEWNET);
+		if (!current) {
+			g_warn_if_reached ();
+			success = FALSE;
+		} else if (_setns (current, CLONE_NEWNET) != 0) {
+			errsv = errno;
+			_LOGE (self, "failed to switch netns: %s", g_strerror (errsv));
+			success = FALSE;
+		}
+	}
+	if (   NM_FLAGS_HAS (ns_types, CLONE_NEWNS)
+	    && (!self || !_stack_current_ns_types (self, CLONE_NEWNS))) {
+		current = _stack_current_netns (CLONE_NEWNS);
+		if (!current) {
+			g_warn_if_reached ();
+			success = FALSE;
+		} else if (_setns (current, CLONE_NEWNS) != 0) {
+			errsv = errno;
+			_LOGE (self, "failed to switch mntns: %s", g_strerror (errsv));
+			success = FALSE;
+		}
+	}
+
+	return success;
+}
+
+/*********************************************************************************************/
+
+int
+nmp_netns_get_fd_net (NMPNetns *self)
+{
+	g_return_val_if_fail (NMP_IS_NETNS (self), 0);
+
+	return self->priv->fd_net;
+}
+
+int
+nmp_netns_get_fd_mnt (NMPNetns *self)
+{
+	g_return_val_if_fail (NMP_IS_NETNS (self), 0);
+
+	return self->priv->fd_mnt;
+}
+
+/*********************************************************************************************/
+
+static gboolean
+_nmp_netns_push_type (NMPNetns *self, int ns_types)
+{
+	NetnsInfo *info;
+	char sbuf[100];
+
+	_stack_ensure_init ();
+
+	info = _stack_peek ();
+	g_return_val_if_fail (info, FALSE);
+
+	if (info->netns == self && info->ns_types == ns_types) {
+		info->count++;
+		_LOGt (self, "push#%u* %s (increase count to %d)",
+		       _stack_size () - 1,
+		       _ns_types_to_str (ns_types, ns_types, sbuf), info->count);
+		return TRUE;
+	}
+
+	_LOGD (self, "push#%u %s",
+	       _stack_size (),
+	       _ns_types_to_str (ns_types,
+	                         _stack_current_ns_types (self, ns_types),
+	                         sbuf));
+
+	if (!_netns_switch_push (self, ns_types))
+		return FALSE;
+
+	_stack_push (self, ns_types);
+	return TRUE;
+}
+
+gboolean
+nmp_netns_push (NMPNetns *self)
+{
+	g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+
+	return _nmp_netns_push_type (self, _CLONE_NS_ALL);
+}
+
+gboolean
+nmp_netns_push_type (NMPNetns *self, int ns_types)
+{
+	g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+	g_return_val_if_fail (!NM_FLAGS_ANY (ns_types, ~_CLONE_NS_ALL), FALSE);
+
+	return _nmp_netns_push_type (self, ns_types == 0 ? _CLONE_NS_ALL : ns_types);
+}
+
+NMPNetns *
+nmp_netns_new (void)
+{
+	NMPNetns *self;
+	int errsv;
+	GError *error = NULL;
+
+	_stack_ensure_init ();
+
+	if (!_stack_peek ()) {
+		/* there are no netns instances. We cannot create a new one
+		 * (because after unshare we couldn't return to the original one). */
+		return NULL;
+	}
+
+	if (unshare (_CLONE_NS_ALL) != 0) {
+		errsv = errno;
+		_LOGE (NULL, "failed to create new net and mnt namespace: %s", g_strerror (errsv));
+		return NULL;
+	}
+
+	if (mount ("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) {
+		errsv = errno;
+		_LOGE (NULL, "failed mount --make-rslave: %s", g_strerror (errsv));
+		goto err_out;
+	}
+
+	if (umount2 ("/sys", MNT_DETACH) != 0) {
+		errsv = errno;
+		_LOGE (NULL, "failed umount /sys: %s", g_strerror (errsv));
+		goto err_out;
+	}
+
+	if (mount ("sysfs", "/sys", "sysfs", 0, NULL) != 0) {
+		errsv = errno;
+		_LOGE (NULL, "failed mount /sys: %s", g_strerror (errsv));
+		goto err_out;
+	}
+
+	self = _netns_new (&error);
+	if (!self) {
+		_LOGE (NULL, "failed to create netns after unshare: %s", error->message);
+		g_clear_error (&error);
+		goto err_out;
+	}
+
+	_stack_push (self, _CLONE_NS_ALL);
+
+	return self;
+err_out:
+	_netns_switch_pop (NULL, _CLONE_NS_ALL);
+	return NULL;
+}
+
+gboolean
+nmp_netns_pop (NMPNetns *self)
+{
+	NetnsInfo *info;
+	int ns_types;
+
+	g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+
+	_stack_ensure_init ();
+
+	info = _stack_peek ();
+
+	g_return_val_if_fail (info, FALSE);
+	g_return_val_if_fail (info->netns == self, FALSE);
+
+	if (info->count > 1) {
+		info->count--;
+		_LOGt (self, "pop#%u* (decrease count to %d)",
+		       _stack_size () - 1, info->count);
+		return TRUE;
+	}
+	g_return_val_if_fail (info->count == 1, FALSE);
+
+	/* cannot pop the original netns. */
+	g_return_val_if_fail (_stack_size () > 1, FALSE);
+
+	_LOGD (self, "pop#%u", _stack_size () - 1);
+
+	ns_types = info->ns_types;
+
+	_stack_pop ();
+
+	return _netns_switch_pop (self, ns_types);
+}
+
+NMPNetns *
+nmp_netns_get_current (void)
+{
+	NetnsInfo *info;
+
+	_stack_ensure_init ();
+
+	info = _stack_peek ();
+	return info ? info->netns : NULL;
+}
+
+NMPNetns *
+nmp_netns_get_initial (void)
+{
+	NetnsInfo *info;
+
+	_stack_ensure_init ();
+
+	info = _stack_bottom ();
+	return info ? info->netns : NULL;
+}
+
+gboolean
+nmp_netns_is_initial (void)
+{
+	if (G_UNLIKELY (!netns_stack))
+		return TRUE;
+
+	return nmp_netns_get_current () == nmp_netns_get_initial ();
+}
+
+/*********************************************************************************************/
+
+gboolean
+nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd)
+{
+	gs_free char *dirname = NULL;
+	int errsv;
+	int fd;
+	nm_auto_pop_netns NMPNetns *netns_pop = NULL;
+
+	g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+	g_return_val_if_fail (filename && filename[0] == '/', FALSE);
+
+	if (!nmp_netns_push_type (self, CLONE_NEWNET))
+		return FALSE;
+	netns_pop = self;
+
+	dirname = g_path_get_dirname (filename);
+	if (mkdir (dirname, 0) != 0) {
+		errsv = errno;
+		if (errsv != EEXIST) {
+			_LOGE (self, "bind: failed to create directory %s: %s",
+			       dirname, g_strerror (errsv));
+			return FALSE;
+		}
+	}
+
+	if ((fd = creat (filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) {
+		errsv = errno;
+		_LOGE (self, "bind: failed to create %s: %s",
+		       filename, g_strerror (errsv));
+		return FALSE;
+	}
+	close (fd);
+
+	if (mount (PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) {
+		errsv = errno;
+		_LOGE (self, "bind: failed to mount %s to %s: %s",
+		       PROC_SELF_NS_NET, filename, g_strerror (errsv));
+		unlink (filename);
+		return FALSE;
+	}
+
+	if (out_fd) {
+		if ((fd = open (filename, O_RDONLY)) == -1) {
+			errsv = errno;
+			_LOGE (self, "bind: failed to open %s: %s", filename, g_strerror (errsv));
+			umount2 (filename, MNT_DETACH);
+			unlink (filename);
+			return FALSE;
+		}
+		*out_fd = fd;
+	}
+
+	return TRUE;
+}
+
+gboolean
+nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename)
+{
+	int errsv;
+
+	g_return_val_if_fail (NMP_IS_NETNS (self), FALSE);
+	g_return_val_if_fail (filename && filename[0] == '/', FALSE);
+
+	if (umount2 (filename, MNT_DETACH) != 0) {
+		errsv = errno;
+		_LOGE (self, "bind: failed to unmount2 %s: %s", filename, g_strerror (errsv));
+		return FALSE;
+	}
+	if (unlink (filename) != 0) {
+		errsv = errno;
+		_LOGE (self, "bind: failed to unlink %s: %s", filename, g_strerror (errsv));
+		return FALSE;
+	}
+	return TRUE;
+}
+
+/******************************************************************************/
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMPNetns *self = NMP_NETNS (object);
+
+	switch (prop_id) {
+	case PROP_FD_NET:
+		/* construct only */
+		self->priv->fd_net = g_value_get_int (value);
+		g_return_if_fail (self->priv->fd_net > 0);
+		break;
+	case PROP_FD_MNT:
+		/* construct only */
+		self->priv->fd_mnt = g_value_get_int (value);
+		g_return_if_fail (self->priv->fd_mnt > 0);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmp_netns_init (NMPNetns *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NMP_TYPE_NETNS, NMPNetnsPrivate);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMPNetns *self = NMP_NETNS (object);
+
+	if (self->priv->fd_net > 0) {
+		close (self->priv->fd_net);
+		self->priv->fd_net = 0;
+	}
+
+	if (self->priv->fd_mnt > 0) {
+		close (self->priv->fd_mnt);
+		self->priv->fd_mnt = 0;
+	}
+
+	G_OBJECT_CLASS (nmp_netns_parent_class)->dispose (object);
+}
+
+static void
+nmp_netns_class_init (NMPNetnsClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (NMPNetnsPrivate));
+
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	obj_properties[PROP_FD_NET]
+	    = g_param_spec_int (NMP_NETNS_FD_NET, "", "",
+	                        0, G_MAXINT, 0,
+	                        G_PARAM_WRITABLE |
+	                        G_PARAM_CONSTRUCT_ONLY |
+	                        G_PARAM_STATIC_STRINGS);
+	obj_properties[PROP_FD_MNT]
+	    = g_param_spec_int (NMP_NETNS_FD_MNT, "", "",
+	                        0, G_MAXINT, 0,
+	                        G_PARAM_WRITABLE |
+	                        G_PARAM_CONSTRUCT_ONLY |
+	                        G_PARAM_STATIC_STRINGS);
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h
new file mode 100644
index 00000000..4eac07ac
--- /dev/null
+++ b/src/platform/nmp-netns.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-platform.c - Handle runtime kernel networking configuration
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ */
+
+#ifndef __NMP_NETNS_UTILS_H__
+#define __NMP_NETNS_UTILS_H__
+
+/*****************************************************************************/
+
+#define NMP_TYPE_NETNS            (nmp_netns_get_type ())
+#define NMP_NETNS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMP_TYPE_NETNS, NMPNetns))
+#define NMP_NETNS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMP_TYPE_NETNS, NMPNetnsClass))
+#define NMP_IS_NETNS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMP_TYPE_NETNS))
+#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMP_TYPE_NETNS))
+#define NMP_NETNS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMP_TYPE_NETNS, NMPNetnsClass))
+
+#define NMP_NETNS_FD_NET          "fd-net"
+#define NMP_NETNS_FD_MNT          "fd-mnt"
+
+struct _NMPNetnsPrivate;
+
+struct _NMPNetns {
+	GObject parent;
+	struct _NMPNetnsPrivate *priv;
+};
+
+typedef struct {
+	GObjectClass parent;
+} NMPNetnsClass;
+
+GType nmp_netns_get_type (void);
+
+NMPNetns *nmp_netns_new (void);
+
+gboolean nmp_netns_push (NMPNetns *self);
+gboolean nmp_netns_push_type (NMPNetns *self, int ns_types);
+gboolean nmp_netns_pop (NMPNetns *self);
+
+NMPNetns *nmp_netns_get_current (void);
+NMPNetns *nmp_netns_get_initial (void);
+gboolean nmp_netns_is_initial (void);
+
+int nmp_netns_get_fd_net (NMPNetns *self);
+int nmp_netns_get_fd_mnt (NMPNetns *self);
+
+static inline void
+_nm_auto_pop_netns (NMPNetns **p)
+{
+	if (*p)
+		nmp_netns_pop (*p);
+}
+
+#define nm_auto_pop_netns __attribute__((cleanup(_nm_auto_pop_netns)))
+
+gboolean nmp_netns_bind_to_path (NMPNetns *self, const char *filename, int *out_fd);
+gboolean nmp_netns_bind_to_path_destroy (NMPNetns *self, const char *filename);
+
+#endif /* __NMP_NETNS_UTILS_H__ */
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index b7b04490..adeb3aab 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -174,10 +174,6 @@ _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev)
 			 * nmp_cache_use_udev_get(). It is on purpose not to test
 			 * for a writable /sys on every call. A minor reason for that is
 			 * performance, but the real reason is reproducibility.
-			 *
-			 * If you want to support changing of whether udev is enabled,
-			 * reset the value via nmp_cache_use_udev_set() carefully -- and
-			 * possibly update the links in the cache accordingly.
 			 * */
 			initialized = TRUE;
 		}
@@ -1192,12 +1188,6 @@ _vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_typ
 /******************************************************************/
 
 gboolean
-nmp_cache_use_udev_detect ()
-{
-	return access ("/sys", W_OK) == 0;
-}
-
-gboolean
 nmp_cache_use_udev_get (const NMPCache *cache)
 {
 	g_return_val_if_fail (cache, TRUE);
@@ -1205,19 +1195,6 @@ nmp_cache_use_udev_get (const NMPCache *cache)
 	return cache->use_udev;
 }
 
-gboolean
-nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev)
-{
-	g_return_val_if_fail (cache, FALSE);
-
-	use_udev = !!use_udev;
-	if (use_udev == cache->use_udev)
-		return FALSE;
-
-	cache->use_udev = use_udev;
-	return TRUE;
-}
-
 /******************************************************************/
 
 /**
@@ -1858,7 +1835,7 @@ nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject
 /******************************************************************/
 
 NMPCache *
-nmp_cache_new ()
+nmp_cache_new (gboolean use_udev)
 {
 	NMPCache *cache = g_new (NMPCache, 1);
 
@@ -1870,7 +1847,7 @@ nmp_cache_new ()
 	                                       (NMMultiIndexFuncEqual) nmp_cache_id_equal,
 	                                       (NMMultiIndexFuncClone) nmp_cache_id_clone,
 	                                       (NMMultiIndexFuncDestroy) nmp_cache_id_destroy);
-	cache->use_udev = nmp_cache_use_udev_detect ();
+	cache->use_udev = !!use_udev;
 	return cache;
 }
 
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 7758798f..6d5b9627 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -399,9 +399,7 @@ GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache,
 gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave);
 const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave);
 
-gboolean nmp_cache_use_udev_detect (void);
 gboolean nmp_cache_use_udev_get (const NMPCache *cache);
-gboolean nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev);
 
 void ASSERT_nmp_cache_is_consistent (const NMPCache *cache);
 
@@ -411,7 +409,7 @@ NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPOb
 NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
 NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
 
-NMPCache *nmp_cache_new (void);
+NMPCache *nmp_cache_new (gboolean use_udev);
 void nmp_cache_free (NMPCache *cache);
 
 #endif /* __NMP_OBJECT_H__ */
diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c
index 3b52a7be..83a0bfd3 100644
--- a/src/platform/tests/test-address.c
+++ b/src/platform/tests/test-address.c
@@ -94,12 +94,12 @@ test_ip4_address_general (void)
 
 	/* Add address */
 	g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 	accept_signal (address_added);
 
 	/* Add address again (aka update) */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, 0, NULL);
 	accept_signals (address_changed, 0, 1);
 
 	/* Test address listing */
@@ -197,7 +197,7 @@ test_ip4_address_general_2 (void)
 	g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, NULL));
 
 	/* Add/delete notification */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
 	accept_signal (address_added);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 	nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr);
@@ -205,7 +205,7 @@ test_ip4_address_general_2 (void)
 	g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 
 	/* Add/delete conflict */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr));
 	accept_signal (address_added);
 
@@ -273,7 +273,7 @@ test_ip4_address_peer (void)
 	accept_signals (address_added, 0, G_MAXINT);
 
 	/* Add/delete notification */
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer, lifetime, preferred, 0, NULL);
 	accept_signal (address_added);
 	a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer);
 	g_assert (a);
@@ -281,7 +281,7 @@ test_ip4_address_peer (void)
 
 	nmtstp_ip_address_assert_lifetime ((NMPlatformIPAddress *) a, -1, lifetime, preferred);
 
-	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer2, lifetime, preferred, NULL);
+	nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer2, lifetime, preferred, 0, NULL);
 	accept_signal (address_added);
 	g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer));
 	a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2);
@@ -328,7 +328,7 @@ test_ip4_address_peer_zero (void)
 	for (i = 0; i < G_N_ELEMENTS (peers); i++) {
 		g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i]));
 
-		nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, label);
+		nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, 0, label);
 
 		addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
 		g_assert (addrs);
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
index 6ad24478..4036daad 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -63,7 +63,7 @@ test_cleanup_internal (void)
 	g_assert (ifindex > 0);
 
 	/* Add routes and addresses */
-	g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, NULL));
+	g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, 0, NULL));
 	g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags));
 	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss));
 	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss));
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 268a5c92..e9fffe90 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -338,30 +338,32 @@ _wait_for_signal_timeout (gpointer user_data)
 }
 
 gboolean
-nmtstp_wait_for_signal (guint timeout_ms)
+nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms)
 {
 	WaitForSignalData data = { 0 };
-
 	gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route;
 
+	if (!platform)
+		platform = NM_PLATFORM_GET;
+
 	data.loop = g_main_loop_new (NULL, FALSE);
 
-	id_link        = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip4_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip6_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip4_route   = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
-	id_ip6_route   = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_link        = g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip4_address = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip6_address = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip4_route   = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
+	id_ip6_route   = g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data);
 
 	if (timeout_ms != 0)
 		data.id = g_timeout_add (timeout_ms, _wait_for_signal_timeout, &data);
 
 	g_main_loop_run (data.loop);
 
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_link));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_address));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_address));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_route));
-	g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_route));
+	g_assert (nm_clear_g_signal_handler (platform, &id_link));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip4_address));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip6_address));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip4_route));
+	g_assert (nm_clear_g_signal_handler (platform, &id_ip6_route));
 
 	if (nm_clear_g_source (&data.id))
 		g_assert (timeout_ms != 0 && !data.timeout);
@@ -372,7 +374,7 @@ nmtstp_wait_for_signal (guint timeout_ms)
 }
 
 gboolean
-nmtstp_wait_for_signal_until (gint64 until_ms)
+nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms)
 {
 	gint64 now;
 
@@ -382,19 +384,19 @@ nmtstp_wait_for_signal_until (gint64 until_ms)
 		if (until_ms < now)
 			return FALSE;
 
-		if (nmtstp_wait_for_signal (MAX (1, until_ms - now)))
+		if (nmtstp_wait_for_signal (platform, MAX (1, until_ms - now)))
 			return TRUE;
 	}
 }
 
 const NMPlatformLink *
-nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
+nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
 {
-	return nmtstp_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
+	return nmtstp_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
 }
 
 const NMPlatformLink *
-nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
+nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
 {
 	const NMPlatformLink *plink;
 	gint64 now;
@@ -402,7 +404,7 @@ nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, g
 	while (TRUE) {
 		now = nm_utils_get_monotonic_timestamp_ms ();
 
-		plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname);
+		plink = nm_platform_link_get_by_ifname (platform ?: NM_PLATFORM_GET, ifname);
 		if (   plink
 		    && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type))
 			return plink;
@@ -410,22 +412,22 @@ nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, g
 		if (until_ms < now)
 			return NULL;
 
-		nmtstp_wait_for_signal (MAX (1, until_ms - now));
+		nmtstp_wait_for_signal (platform, MAX (1, until_ms - now));
 	}
 }
 
 const NMPlatformLink *
-nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
+nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms)
 {
-	return nmtstp_assert_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
+	return nmtstp_assert_wait_for_link_until (platform, ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms);
 }
 
 const NMPlatformLink *
-nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
+nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms)
 {
 	const NMPlatformLink *plink;
 
-	plink = nmtstp_wait_for_link_until (ifname, expected_link_type, until_ms);
+	plink = nmtstp_wait_for_link_until (platform, ifname, expected_link_type, until_ms);
 	g_assert (plink);
 	return plink;
 }
@@ -563,8 +565,8 @@ _ip_address_add (gboolean external_command,
                  const NMIPAddr *peer_address,
                  guint32 lifetime,
                  guint32 preferred,
-                 const char *label,
-                 guint32 flags)
+                 guint32 flags,
+                 const char *label)
 {
 	gint64 end_time;
 
@@ -626,7 +628,6 @@ _ip_address_add (gboolean external_command,
 		gboolean success;
 
 		if (is_v4) {
-			g_assert (flags == 0);
 			success = nm_platform_ip4_address_add (NM_PLATFORM_GET,
 			                                       ifindex,
 			                                       address->addr4,
@@ -634,6 +635,7 @@ _ip_address_add (gboolean external_command,
 			                                       peer_address->addr4,
 			                                       lifetime,
 			                                       preferred,
+			                                       flags,
 			                                       label);
 		} else {
 			g_assert (label == NULL);
@@ -686,20 +688,20 @@ _ip_address_add (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
-#define _assert_pllink(success, pllink, name, type) \
+#define _assert_pllink(platform, success, pllink, name, type) \
 	G_STMT_START { \
 		const NMPlatformLink *_pllink = (pllink); \
 		\
 		if ((success)) { \
 			g_assert (_pllink); \
-			g_assert (_pllink == nmtstp_link_get_typed (_pllink->ifindex, (name), (type))); \
+			g_assert (_pllink == nmtstp_link_get_typed (platform, _pllink->ifindex, (name), (type))); \
 		} else { \
 			g_assert (!_pllink); \
-			g_assert (!nmtstp_link_get (0, (name))); \
+			g_assert (!nmtstp_link_get (platform, 0, (name))); \
 		} \
 	} G_STMT_END
 
@@ -718,12 +720,12 @@ nmtstp_link_dummy_add (gboolean external_command,
 		success = !nmtstp_run_command ("ip link add %s type dummy",
 		                                name);
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_DUMMY, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_DUMMY, 100);
 	} else
 		success = nm_platform_link_dummy_add (NM_PLATFORM_GET, name, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
 	g_assert (success);
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_DUMMY);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_DUMMY);
 	return pllink;
 }
 
@@ -755,11 +757,11 @@ nmtstp_link_gre_add (gboolean external_command,
 		                                lnk->tos,
 		                                lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_GRE, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_GRE, 100);
 	} else
 		success = nm_platform_link_gre_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_GRE);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_GRE);
 
 	return pllink;
 }
@@ -806,11 +808,11 @@ nmtstp_link_ip6tnl_add (gboolean external_command,
 		                                lnk->encap_limit,
 		                                lnk->flow_label);
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IP6TNL, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_IP6TNL, 100);
 	} else
 		success = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_IP6TNL);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_IP6TNL);
 
 	return pllink;
 }
@@ -843,11 +845,11 @@ nmtstp_link_ipip_add (gboolean external_command,
 		                                lnk->tos,
 		                                lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IPIP, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_IPIP, 100);
 	} else
 		success = nm_platform_link_ipip_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_IPIP);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_IPIP);
 
 	return pllink;
 }
@@ -888,11 +890,11 @@ nmtstp_link_macvlan_add (gboolean external_command,
 		                                modes[lnk->mode],
 		                                lnk->no_promisc ? "nopromisc" : "");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, link_type, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, link_type, 100);
 	} else
 		success = nm_platform_link_macvlan_add (NM_PLATFORM_GET, name, parent, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, link_type);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, link_type);
 
 	return pllink;
 }
@@ -930,11 +932,11 @@ nmtstp_link_sit_add (gboolean external_command,
 		                                lnk->tos,
 		                                lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc");
 		if (success)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_SIT, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_SIT, 100);
 	} else
 		success = nm_platform_link_sit_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS;
 
-	_assert_pllink (success, pllink, name, NM_LINK_TYPE_SIT);
+	_assert_pllink (NM_PLATFORM_GET, success, pllink, name, NM_LINK_TYPE_SIT);
 
 	return pllink;
 }
@@ -983,7 +985,7 @@ nmtstp_link_vxlan_add (gboolean external_command,
 		/* Older versions of iproute2 don't support adding vxlan devices.
 		 * On failure, fallback to using platform code. */
 		if (err == 0)
-			pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_VXLAN, 100);
+			pllink = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, name, NM_LINK_TYPE_VXLAN, 100);
 		else
 			_LOGI ("Adding vxlan device via iproute2 failed. Assume iproute2 is not up to the task.");
 	}
@@ -1006,6 +1008,7 @@ nmtstp_ip4_address_add (gboolean external_command,
                         in_addr_t peer_address,
                         guint32 lifetime,
                         guint32 preferred,
+                        guint32 flags,
                         const char *label)
 {
 	_ip_address_add (external_command,
@@ -1016,8 +1019,8 @@ nmtstp_ip4_address_add (gboolean external_command,
 	                 (NMIPAddr *) &peer_address,
 	                 lifetime,
 	                 preferred,
-	                 label,
-	                 0);
+	                 flags,
+	                 label);
 }
 
 void
@@ -1038,8 +1041,8 @@ nmtstp_ip6_address_add (gboolean external_command,
 	                 (NMIPAddr *) &peer_address,
 	                 lifetime,
 	                 preferred,
-	                 NULL,
-	                 flags);
+	                 flags,
+	                 NULL);
 }
 
 static void
@@ -1127,7 +1130,7 @@ _ip_address_del (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
@@ -1161,14 +1164,18 @@ nmtstp_ip6_address_del (gboolean external_command,
 }
 
 const NMPlatformLink *
-nmtstp_link_get_typed (int ifindex,
+nmtstp_link_get_typed (NMPlatform *platform,
+                       int ifindex,
                        const char *name,
                        NMLinkType link_type)
 {
 	const NMPlatformLink *pllink = NULL;
 
+	if (!platform)
+		platform = NM_PLATFORM_GET;
+
 	if (ifindex > 0) {
-		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+		pllink = nm_platform_link_get (platform, ifindex);
 
 		if (pllink) {
 			g_assert_cmpint (pllink->ifindex, ==, ifindex);
@@ -1176,12 +1183,12 @@ nmtstp_link_get_typed (int ifindex,
 				g_assert_cmpstr (name, ==, pllink->name);
 		} else {
 			if (name)
-				g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name));
+				g_assert (!nm_platform_link_get_by_ifname (platform, name));
 		}
 	} else {
 		g_assert (name);
 
-		pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name);
+		pllink = nm_platform_link_get_by_ifname (platform, name);
 
 		if (pllink)
 			g_assert_cmpstr (name, ==, pllink->name);
@@ -1196,10 +1203,11 @@ nmtstp_link_get_typed (int ifindex,
 }
 
 const NMPlatformLink *
-nmtstp_link_get (int ifindex,
+nmtstp_link_get (NMPlatform *platform,
+                 int ifindex,
                  const char *name)
 {
-	return nmtstp_link_get_typed (ifindex, name, NM_LINK_TYPE_NONE);
+	return nmtstp_link_get_typed (platform, ifindex, name, NM_LINK_TYPE_NONE);
 }
 
 void
@@ -1212,7 +1220,7 @@ nmtstp_link_del (gboolean external_command,
 	gboolean success;
 	gs_free char *name_copy = NULL;
 
-	pllink = nmtstp_link_get (ifindex, name);
+	pllink = nmtstp_link_get (NM_PLATFORM_GET, ifindex, name);
 
 	g_assert (pllink);
 
@@ -1242,7 +1250,7 @@ nmtstp_link_del (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
@@ -1288,7 +1296,7 @@ nmtstp_link_set_updown (gboolean external_command,
 		/* for internal command, we expect not to reach this line.*/
 		g_assert (external_command);
 
-		g_assert (nmtstp_wait_for_signal_until (end_time));
+		g_assert (nmtstp_wait_for_signal_until (NM_PLATFORM_GET, end_time));
 	} while (TRUE);
 }
 
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 0281d3c5..82561193 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -91,13 +91,13 @@ void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, N
 int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__printf__, 1, 2)));
 #define nmtstp_run_command_check(...) do { g_assert_cmpint (nmtstp_run_command (__VA_ARGS__), ==, 0); } while (0)
 
-gboolean nmtstp_wait_for_signal (guint timeout_ms);
-gboolean nmtstp_wait_for_signal_until (gint64 until_ms);
-const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
-const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
+gboolean nmtstp_wait_for_signal (NMPlatform *platform, guint timeout_ms);
+gboolean nmtstp_wait_for_signal_until (NMPlatform *platform, gint64 until_ms);
+const NMPlatformLink *nmtstp_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
+const NMPlatformLink *nmtstp_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
 
-const NMPlatformLink *nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
-const NMPlatformLink *nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
+const NMPlatformLink *nmtstp_assert_wait_for_link (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, guint timeout_ms);
+const NMPlatformLink *nmtstp_assert_wait_for_link_until (NMPlatform *platform, const char *ifname, NMLinkType expected_link_type, gint64 until_ms);
 
 int nmtstp_run_command_check_external_global (void);
 gboolean nmtstp_run_command_check_external (int external_command);
@@ -117,6 +117,7 @@ void nmtstp_ip4_address_add (gboolean external_command,
                              in_addr_t peer_address,
                              guint32 lifetime,
                              guint32 preferred,
+                             guint32 flags,
                              const char *label);
 void nmtstp_ip6_address_add (gboolean external_command,
                              int ifindex,
@@ -125,7 +126,7 @@ void nmtstp_ip6_address_add (gboolean external_command,
                              struct in6_addr peer_address,
                              guint32 lifetime,
                              guint32 preferred,
-                             guint flags);
+                             guint32 flags);
 void nmtstp_ip4_address_del (gboolean external_command,
                              int ifindex,
                              in_addr_t address,
@@ -136,8 +137,8 @@ void nmtstp_ip6_address_del (gboolean external_command,
                              struct in6_addr address,
                              int plen);
 
-const NMPlatformLink *nmtstp_link_get_typed (int ifindex, const char *name, NMLinkType link_type);
-const NMPlatformLink *nmtstp_link_get (int ifindex, const char *name);
+const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type);
+const NMPlatformLink *nmtstp_link_get (NMPlatform *platform, int ifindex, const char *name);
 
 void nmtstp_link_set_updown (gboolean external_command,
                              int ifindex,
diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c
index f342e565..66f72fa5 100644
--- a/src/platform/tests/test-general.c
+++ b/src/platform/tests/test-general.c
@@ -18,12 +18,12 @@
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
-#include "nm-platform-utils.h"
+#include "nm-default.h"
 
 #include <linux/rtnetlink.h>
 
+#include "nm-platform-utils.h"
 #include "nm-linux-platform.h"
-#include "nm-default.h"
 
 #include "nm-test-utils.h"
 
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 3fa5ad50..94695edc 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -21,8 +21,13 @@
 #include "nm-default.h"
 
 #include <sched.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include "nmp-object.h"
+#include "nmp-netns.h"
+#include "nm-platform-utils.h"
 
 #include "test-common.h"
 #include "nm-test-utils.h"
@@ -40,6 +45,9 @@
 #define VLAN_FLAGS 0
 #define MTU 1357
 
+#define _ADD_DUMMY(platform, name) \
+	g_assert_cmpint (nm_platform_link_dummy_add ((platform), (name), NULL), ==, NM_PLATFORM_ERROR_SUCCESS)
+
 static void
 test_bogus(void)
 {
@@ -52,16 +60,16 @@ test_bogus(void)
 	g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX));
 	g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX));
 
 	g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -72,7 +80,7 @@ test_bogus(void)
 	g_assert (!addrlen);
 	g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*failure changing link: *");
 	g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU));
 
 	g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -690,7 +698,7 @@ test_software_detect (gconstpointer user_data)
 	const gboolean ext = test_data->external_command;
 
 	nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME);
-	ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	switch (test_data->link_type) {
 	case NM_LINK_TYPE_GRE: {
@@ -852,7 +860,7 @@ test_software_detect (gconstpointer user_data)
 		g_assert_not_reached ();
 	}
 
-	ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, test_data->link_type, 100)->ifindex;
+	ifindex = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, DEVICE_NAME, test_data->link_type, 100)->ifindex;
 
 	nmtstp_link_set_updown (-1, ifindex_parent, TRUE);
 
@@ -1137,10 +1145,10 @@ test_vlan_set_xgress (void)
 	int ifindex, ifindex_parent;
 
 	nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME);
-	ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_parent = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1245", DEVICE_NAME, PARENT_NAME);
-	ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex;
+	ifindex = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex;
 
 	/* ingress-qos-map */
 
@@ -1662,7 +1670,7 @@ test_create_many_links (gconstpointer user_data)
 	guint n_devices = GPOINTER_TO_UINT (user_data);
 
 	if (n_devices > 100 && nmtst_test_quick ()) {
-		g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", str_if_set (g_get_prgname (), "test-link-linux"));
+		g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", g_get_prgname () ?: "test-link-linux");
 		g_test_skip ("Skip long running test");
 		return;
 	}
@@ -1685,8 +1693,8 @@ test_nl_bugs_veth (void)
 
 	/* create veth pair. */
 	nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1);
-	ifindex_veth0 = nmtstp_assert_wait_for_link (IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
-	ifindex_veth1 = nmtstp_assert_wait_for_link (IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
+	ifindex_veth0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex;
+	ifindex_veth1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex;
 
 	/* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */
 	g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i));
@@ -1722,7 +1730,7 @@ test_nl_bugs_veth (void)
 
 	nmtstp_run_command_check ("ip link set %s netns %ld", IFACE_VETH1, (long) nmtstp_namespace_handle_get_pid (ns_handle));
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (50);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 		nm_platform_process_events (NM_PLATFORM_GET);
 
 		pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1);
@@ -1736,8 +1744,8 @@ test_nl_bugs_veth (void)
 
 out:
 	nmtstp_link_del (-1, ifindex_veth0, IFACE_VETH0);
-	g_assert (!nmtstp_link_get (ifindex_veth0, IFACE_VETH0));
-	g_assert (!nmtstp_link_get (ifindex_veth1, IFACE_VETH1));
+	g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_veth0, IFACE_VETH0));
+	g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_veth1, IFACE_VETH1));
 	nmtstp_namespace_handle_release (ns_handle);
 }
 
@@ -1755,16 +1763,16 @@ test_nl_bugs_spuroius_newlink (void)
 	/* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */
 
 	nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0);
-	ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_dummy0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	nmtstp_run_command_check ("ip link add %s type bond", IFACE_BOND0);
-	ifindex_bond0 = nmtstp_assert_wait_for_link (IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex;
+	ifindex_bond0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex;
 
 	nmtstp_link_set_updown (-1, ifindex_bond0, TRUE);
 
 	nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BOND0);
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (50);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 
 		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
 		g_assert (pllink);
@@ -1775,7 +1783,7 @@ test_nl_bugs_spuroius_newlink (void)
 	nmtstp_run_command_check ("ip link del %s",  IFACE_BOND0);
 
 	wait_for_settle = TRUE;
-	nmtstp_wait_for_signal (50);
+	nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 again:
 	nm_platform_process_events (NM_PLATFORM_GET);
 	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bond0);
@@ -1783,11 +1791,11 @@ again:
 
 	if (wait_for_settle) {
 		wait_for_settle = FALSE;
-		NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
+		NMTST_WAIT (300, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); });
 		goto again;
 	}
 
-	g_assert (!nmtstp_link_get (ifindex_bond0, IFACE_BOND0));
+	g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_bond0, IFACE_BOND0));
 	nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
 }
 
@@ -1805,16 +1813,16 @@ test_nl_bugs_spuroius_dellink (void)
 	/* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */
 
 	nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0);
-	ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
+	ifindex_dummy0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex;
 
 	nmtstp_run_command_check ("ip link add %s type bridge", IFACE_BRIDGE0);
-	ifindex_bridge0 = nmtstp_assert_wait_for_link (IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex;
+	ifindex_bridge0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex;
 
 	nmtstp_link_set_updown (-1, ifindex_bridge0, TRUE);
 
 	nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BRIDGE0);
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (50);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 
 		pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0);
 		g_assert (pllink);
@@ -1827,7 +1835,7 @@ test_nl_bugs_spuroius_dellink (void)
 	nmtstp_run_command_check ("ip link set %s nomaster",  IFACE_DUMMY0);
 
 	wait_for_settle = TRUE;
-	nmtstp_wait_for_signal (50);
+	nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 again:
 	nm_platform_process_events (NM_PLATFORM_GET);
 	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bridge0);
@@ -1838,7 +1846,7 @@ again:
 
 	if (wait_for_settle) {
 		wait_for_settle = FALSE;
-		NMTST_WAIT (300, { nmtstp_wait_for_signal (50); });
+		NMTST_WAIT (300, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); });
 		goto again;
 	}
 
@@ -1846,6 +1854,442 @@ again:
 	nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0);
 }
 
+/******************************************************************/
+
+static void
+_test_netns_setup (gpointer fixture, gconstpointer test_data)
+{
+	/* the singleton platform instance has netns support disabled.
+	 * Destroy the instance before the test and re-create it afterwards. */
+	g_object_unref (nm_platform_get ());
+}
+
+static void
+_test_netns_teardown (gpointer fixture, gconstpointer test_data)
+{
+	/* re-create platform instance */
+	SETUP ();
+}
+
+static NMPlatform *
+_test_netns_create_platform (void)
+{
+	NMPNetns *netns;
+	NMPlatform *platform;
+
+	netns = nmp_netns_new ();
+	g_assert (NMP_IS_NETNS (netns));
+
+	platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	g_assert (NM_IS_LINUX_PLATFORM (platform));
+
+	nmp_netns_pop (netns);
+	g_object_unref (netns);
+
+	return platform;
+}
+
+static gboolean
+_test_netns_check_skip (void)
+{
+	static int support = -1;
+	static int support_errsv = 0;
+	NMPNetns *netns;
+
+	netns = nmp_netns_get_current ();
+	if (!netns) {
+		g_test_skip ("No netns support");
+		return TRUE;
+	}
+
+	g_assert (nmp_netns_get_fd_net (netns) > 0);
+
+	if (support == -1) {
+		support = (setns (nmp_netns_get_fd_net (netns), CLONE_NEWNET) == 0);
+		if (!support)
+			support_errsv = errno;
+	}
+	if (!support) {
+			_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", g_strerror (support_errsv));
+			g_test_skip ("No netns support (setns failed)");
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/******************************************************************/
+
+static void
+test_netns_general (gpointer fixture, gconstpointer test_data)
+{
+	gs_unref_object NMPlatform *platform_1 = NULL;
+	gs_unref_object NMPlatform *platform_2 = NULL;
+	NMPNetns *netns_tmp;
+	char sbuf[100];
+	int i, j, k;
+	gboolean ethtool_support;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	platform_1 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	platform_2 = _test_netns_create_platform ();
+
+	/* add some dummy devices. The "other-*" devices are there to bump the ifindex */
+	for (k = 0; k < 2; k++) {
+		NMPlatform *p = (k == 0 ? platform_1 : platform_2);
+		const char *id = (k == 0 ? "a" : "b");
+
+		for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
+			_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-a-%s-%02d", id, i));
+
+		_ADD_DUMMY (p, "dummy1_");
+
+		for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
+			_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-b-%s-%02d", id, i));
+
+		_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "dummy2%s", id));
+
+		for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++)
+			_ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-c-%s-%02d", id, i));
+	}
+
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex));
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, NULL);
+
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy1_/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex));
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2a/ifindex"), ==, NULL);
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/sys/devices/virtual/net/dummy2b/ifindex"), ==, nm_sprintf_buf (sbuf, "%d", nmtstp_link_get_typed (platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex));
+
+	for (i = 0; i < 10; i++) {
+		NMPlatform *pl;
+		const char *path;
+
+		j = nmtst_get_rand_int () % 2;
+
+		if (nmtst_get_rand_int () % 2) {
+			pl = platform_1;
+			if (nmtst_get_rand_int () % 2)
+				path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6";
+			else
+				path = "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6";
+		} else {
+			pl = platform_2;
+			if (nmtst_get_rand_int () % 2)
+				path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6";
+			else
+				path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6";
+		}
+		g_assert (nm_platform_sysctl_set (pl, path, nm_sprintf_buf (sbuf, "%d", j)));
+		g_assert_cmpstr (nm_platform_sysctl_get (pl, path), ==, nm_sprintf_buf (sbuf, "%d", j));
+	}
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"), ==, NULL);
+	g_assert_cmpstr (nm_platform_sysctl_get (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"), ==, NULL);
+
+	/* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and
+	 * skip asserts that are known to fail. */
+	ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0;
+	if (ethtool_support) {
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL));
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL));
+		g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a > /dev/null"), ==, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b 2> /dev/null"), !=, 0);
+	}
+
+	g_assert (nm_platform_netns_push (platform_2, &netns_tmp));
+
+	if (ethtool_support) {
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy1_", NULL, NULL, NULL));
+		g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL));
+		g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL));
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy1_ > /dev/null"), ==, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2a 2> /dev/null"), !=, 0);
+		g_assert_cmpint (nmtstp_run_command ("ethtool -i dummy2b > /dev/null"), ==, 0);
+	}
+
+	nmp_netns_pop (netns_tmp);
+}
+
+/*****************************************************************************/
+
+static void
+test_netns_set_netns (gpointer fixture, gconstpointer test_data)
+{
+	NMPlatform *platforms[3];
+	gs_unref_object NMPlatform *platform_0 = NULL;
+	gs_unref_object NMPlatform *platform_1 = NULL;
+	gs_unref_object NMPlatform *platform_2 = NULL;
+	nm_auto_pop_netns NMPNetns *netns_pop = NULL;
+	int i;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	platforms[0] = platform_0 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	platforms[1] = platform_1 = _test_netns_create_platform ();
+	platforms[2] = platform_2 = _test_netns_create_platform ();
+
+	i = nmtst_get_rand_int () % 4;
+	if (i != 3)
+		g_assert (nm_platform_netns_push (platforms[i], &netns_pop));
+
+#define LINK_MOVE_NAME "link-move"
+	g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert (!nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+	_ADD_DUMMY (platform_1, LINK_MOVE_NAME);
+	g_assert ( nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert (!nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+	g_assert (nm_platform_link_set_netns (platform_1,
+	                                      nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME)->ifindex,
+	                                      nmp_netns_get_fd_net (nm_platform_netns_get (platform_2))));
+	g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert (!nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+	nmtstp_assert_wait_for_link (platform_2, LINK_MOVE_NAME, NM_LINK_TYPE_DUMMY, 100);
+	g_assert (!nm_platform_link_get_by_ifname (platform_1, LINK_MOVE_NAME));
+	g_assert ( nm_platform_link_get_by_ifname (platform_2, LINK_MOVE_NAME));
+}
+
+/*****************************************************************************/
+
+static char *
+_get_current_namespace_id (int ns_type)
+{
+	const char *p;
+	GError *error = NULL;
+	char *id;
+
+	switch (ns_type) {
+	case CLONE_NEWNET:
+		p = "/proc/self/ns/net";
+		break;
+	case CLONE_NEWNS:
+		p = "/proc/self/ns/mnt";
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	id = g_file_read_link (p, &error);
+	g_assert_no_error (error);
+	g_assert (id);
+	return id;
+}
+
+static char *
+_get_sysctl_value (const char *path)
+{
+	char *data = NULL;
+	gs_free_error GError *error = NULL;
+
+	if (!g_file_get_contents (path, &data, NULL, &error)) {
+		nmtst_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, NULL);
+		g_assert (!data);
+	} else {
+		g_assert_no_error (error);
+		g_assert (data);
+		g_strstrip (data);
+	}
+	return data;
+}
+
+static void
+test_netns_push (gpointer fixture, gconstpointer test_data)
+{
+	gs_unref_object NMPlatform *platform_0 = NULL;
+	gs_unref_object NMPlatform *platform_1 = NULL;
+	gs_unref_object NMPlatform *platform_2 = NULL;
+	nm_auto_pop_netns NMPNetns *netns_pop = NULL;
+	gs_unref_ptrarray GPtrArray *device_names = g_ptr_array_new_with_free_func (g_free);
+	int i, j;
+	const int ns_types_list[] = { CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWNET | CLONE_NEWNS };
+	const int ns_types_test[] = { CLONE_NEWNET, CLONE_NEWNS };
+	typedef struct {
+		NMPlatform *platform;
+		const char *device_name;
+		const char *sysctl_path;
+		const char *sysctl_value;
+		const char *ns_net;
+		const char *ns_mnt;
+	} PlatformData;
+	PlatformData pl[3] = { };
+	PlatformData *pl_base;
+	struct {
+		PlatformData *pl;
+		int ns_types;
+	} stack[6] = { };
+	int nstack;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	pl[0].platform = platform_0 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	pl[1].platform = platform_1 = _test_netns_create_platform ();
+	pl[2].platform = platform_2 = _test_netns_create_platform ();
+
+	pl_base = &pl[0];
+	i = nmtst_get_rand_int () % (G_N_ELEMENTS (pl) + 1);
+	if (i < G_N_ELEMENTS (pl)) {
+		pl_base = &pl[i];
+		g_assert (nm_platform_netns_push (pl[i].platform, &netns_pop));
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (pl); i++) {
+		nm_auto_pop_netns NMPNetns *netns_free = NULL;
+		char *tmp;
+
+		g_assert (nm_platform_netns_push (pl[i].platform, &netns_free));
+
+		tmp = g_strdup_printf ("nmtst-dev-%d", i);
+		g_ptr_array_add (device_names, tmp);
+		pl[i].device_name = tmp;
+
+		tmp = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6", pl[i].device_name);
+		g_ptr_array_add (device_names, tmp);
+		pl[i].sysctl_path = tmp;
+
+		pl[i].sysctl_value = nmtst_get_rand_int () % 2 ? "1" : "0";
+
+		_ADD_DUMMY (pl[i].platform, pl[i].device_name);
+
+		g_assert (nm_platform_sysctl_set (pl[i].platform, pl[i].sysctl_path, pl[i].sysctl_value));
+
+		tmp = _get_current_namespace_id (CLONE_NEWNET);
+		g_ptr_array_add (device_names, tmp);
+		pl[i].ns_net = tmp;
+
+		tmp = _get_current_namespace_id (CLONE_NEWNS);
+		g_ptr_array_add (device_names, tmp);
+		pl[i].ns_mnt = tmp;
+	}
+
+	nstack = nmtst_get_rand_int () % (G_N_ELEMENTS (stack) + 1);
+	for (i = 0; i < nstack; i++) {
+		stack[i].pl = &pl[nmtst_get_rand_int () % G_N_ELEMENTS (pl)];
+		stack[i].ns_types = ns_types_list[nmtst_get_rand_int () % G_N_ELEMENTS (ns_types_list)];
+
+		nmp_netns_push_type (nm_platform_netns_get (stack[i].pl->platform), stack[i].ns_types);
+	}
+
+	/* pop some again. */
+	for (i = nmtst_get_rand_int () % (nstack + 1); i > 0; i--) {
+		g_assert (nstack > 0);
+		nstack--;
+		nmp_netns_pop (nm_platform_netns_get (stack[nstack].pl->platform));
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (ns_types_test); i++) {
+		int ns_type = ns_types_test[i];
+		PlatformData *p;
+		gs_free char *current_namespace_id = NULL;
+
+		p = pl_base;
+		for (j = nstack; j >= 1; ) {
+			j--;
+			if (NM_FLAGS_HAS (stack[j].ns_types, ns_type)) {
+				p = stack[j].pl;
+				break;
+			}
+		}
+
+		current_namespace_id = _get_current_namespace_id (ns_type);
+
+		if (ns_type == CLONE_NEWNET) {
+			g_assert_cmpstr (current_namespace_id, ==, p->ns_net);
+			for (j = 0; j < G_N_ELEMENTS (pl); j++) {
+				gs_free char *data = NULL;
+
+				if (p == &pl[j])
+					g_assert_cmpint (nmtstp_run_command ("ip link show %s 1>/dev/null", pl[j].device_name), ==, 0);
+				else
+					g_assert_cmpint (nmtstp_run_command ("ip link show %s 2>/dev/null", pl[j].device_name), !=, 0);
+
+				data = _get_sysctl_value (pl[j].sysctl_path);
+				if (p == &pl[j])
+					g_assert_cmpstr (data, ==, pl[j].sysctl_value);
+				else
+					g_assert (!data);
+			}
+		} else if (ns_type == CLONE_NEWNS) {
+			g_assert_cmpstr (current_namespace_id, ==, p->ns_mnt);
+			for (j = 0; j < G_N_ELEMENTS (pl); j++) {
+				char path[600];
+				gs_free char *data = NULL;
+
+				nm_sprintf_buf (path, "/sys/devices/virtual/net/%s/ifindex", pl[j].device_name);
+
+				data = _get_sysctl_value (path);
+				if (p == &pl[j])
+					g_assert_cmpstr (data, ==, nm_sprintf_buf (path, "%d", nmtstp_link_get_typed (p->platform, 0, p->device_name, NM_LINK_TYPE_DUMMY)->ifindex));
+				else
+					g_assert (!data);
+			}
+		} else
+			g_assert_not_reached ();
+	}
+
+
+	for (i = nstack; i >= 1; ) {
+		i--;
+		nmp_netns_pop (nm_platform_netns_get (stack[i].pl->platform));
+	}
+}
+
+/*****************************************************************************/
+
+static void
+test_netns_bind_to_path (gpointer fixture, gconstpointer test_data)
+{
+#define P_VAR_RUN                "/var/run"
+#define P_VAR_RUN_NETNS          "/var/run/netns"
+#define P_VAR_RUN_NETNS_BINDNAME "/var/run/netns/"P_NETNS_BINDNAME
+#define P_NETNS_BINDNAME         "nmtst-iproute2-netns"
+	gs_unref_object NMPlatform *platform_0 = NULL;
+	gs_unref_object NMPlatform *platform_1 = NULL;
+	gs_unref_object NMPlatform *platform_2 = NULL;
+	nm_auto_pop_netns NMPNetns *netns_pop = NULL;
+	NMPlatform *platforms[3];
+	NMPNetns *netns;
+	int i;
+
+	if (_test_netns_check_skip ())
+		return;
+
+	platforms[0] = platform_0 = g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_NETNS_SUPPORT, TRUE, NULL);
+	platforms[1] = platform_1 = _test_netns_create_platform ();
+	platforms[2] = platform_2 = _test_netns_create_platform ();
+
+	i = nmtst_get_rand_int () % 4;
+	if (i != 3)
+		g_assert (nm_platform_netns_push (platforms[i], &netns_pop));
+
+	g_assert_cmpint (mount ("tmpfs", P_VAR_RUN, "tmpfs", MS_NOATIME | MS_NODEV | MS_NOSUID, "mode=0755,size=32K"), ==, 0);
+	g_assert_cmpint (mkdir (P_VAR_RUN_NETNS, 755), ==, 0);
+
+	i = (nmtst_get_rand_int () % 2) + 1;
+	netns = nm_platform_netns_get (platforms[i]);
+
+	_ADD_DUMMY (platforms[i], "dummy2b");
+
+	g_assert (!g_file_test (P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" true 2>/dev/null"), !=, 0);
+
+	g_assert (nmp_netns_bind_to_path (netns, P_VAR_RUN_NETNS_BINDNAME, NULL));
+
+	g_assert (g_file_test (P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" true"), ==, 0);
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" ip link show dummy2b 1>/dev/null"), ==, 0);
+
+	g_assert (nmp_netns_bind_to_path_destroy (netns, P_VAR_RUN_NETNS_BINDNAME));
+
+	g_assert (!g_file_test (P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS));
+	g_assert_cmpint (nmtstp_run_command ("ip netns exec "P_NETNS_BINDNAME" true 2>/dev/null"), !=, 0);
+
+	g_assert_cmpint (umount (P_VAR_RUN), ==, 0);
+}
+
 /*****************************************************************************/
 
 void
@@ -1894,5 +2338,10 @@ setup_tests (void)
 		g_test_add_func ("/link/nl-bugs/veth", test_nl_bugs_veth);
 		g_test_add_func ("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink);
 		g_test_add_func ("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink);
+
+		g_test_add_vtable ("/general/netns/general", 0, NULL, _test_netns_setup, test_netns_general, _test_netns_teardown);
+		g_test_add_vtable ("/general/netns/set-netns", 0, NULL, _test_netns_setup, test_netns_set_netns, _test_netns_teardown);
+		g_test_add_vtable ("/general/netns/push", 0, NULL, _test_netns_setup, test_netns_push, _test_netns_teardown);
+		g_test_add_vtable ("/general/netns/bind-to-path", 0, NULL, _test_netns_setup, test_netns_bind_to_path, _test_netns_teardown);
 	}
 }
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index 3b44abb8..d77170b3 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -223,9 +223,7 @@ test_cache_link (void)
 	GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
 	NMPCacheOpsType ops_type;
 
-	cache = nmp_cache_new ();
-
-	nmp_cache_use_udev_set (cache, g_rand_int_range (nmtst_get_rand (), 0, 2));
+	cache = nmp_cache_new (nmtst_get_rand_int () % 2);
 
 	/* if we have a link, and don't set is_in_netlink, adding it has no effect. */
 	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index f5fa024b..85851854 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -314,7 +314,7 @@ test_ip4_zero_gateway (void)
 	nmtstp_run_command_check ("ip route add 1.2.3.2/32 dev %s", DEVICE_NAME);
 
 	NMTST_WAIT_ASSERT (100, {
-		nmtstp_wait_for_signal (10);
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 10);
 		if (   nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0)
 		    && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0))
 			break;
@@ -322,7 +322,7 @@ test_ip4_zero_gateway (void)
 
 	nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME);
 
-	nmtstp_wait_for_signal (50);
+	nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
 	nm_platform_process_events (NM_PLATFORM_GET);
 }
 
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
index 029601e4..af285b45 100644
--- a/src/platform/wifi/wifi-utils-wext.c
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -101,7 +101,7 @@ wifi_wext_get_mode (WifiData *data)
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 
 	if (ioctl (wext->fd, SIOCGIWMODE, &wrq) < 0) {
 		if (errno != ENODEV) {
@@ -118,6 +118,7 @@ wifi_wext_get_mode (WifiData *data)
 	case IW_MODE_MASTER:
 		return NM_802_11_MODE_AP;
 	case IW_MODE_INFRA:
+	case IW_MODE_AUTO: /* hack for WEXT devices reporting IW_MODE_AUTO */
 		return NM_802_11_MODE_INFRA;
 	default:
 		break;
@@ -150,7 +151,7 @@ wifi_wext_set_mode (WifiData *data, const NM80211Mode mode)
 		return FALSE;
 	}
 
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	if (ioctl (wext->fd, SIOCSIWMODE, &wrq) < 0) {
 		if (errno != ENODEV) {
 			nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): error setting mode %d",
@@ -174,7 +175,7 @@ wifi_wext_set_powersave (WifiData *data, guint32 powersave)
 	} else
 		wrq.u.power.disabled = 1;
 
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	if (ioctl (wext->fd, SIOCSIWPOWER, &wrq) < 0) {
 		if (errno != ENODEV) {
 			nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): error setting powersave %" G_GUINT32_FORMAT,
@@ -193,7 +194,7 @@ wifi_wext_get_freq (WifiData *data)
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	if (ioctl (wext->fd, SIOCGIWFREQ, &wrq) < 0) {
 		nm_log_warn (LOGD_HW | LOGD_WIFI,
 		             "(%s): error getting frequency: %s",
@@ -227,7 +228,7 @@ wifi_wext_get_bssid (WifiData *data, guint8 *out_bssid)
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (wrq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	if (ioctl (wext->fd, SIOCGIWAP, &wrq) < 0) {
 		nm_log_warn (LOGD_HW | LOGD_WIFI,
 		             "(%s): error getting associated BSSID: %s",
@@ -246,7 +247,7 @@ wifi_wext_get_rate (WifiData *data)
 	int err;
 
 	memset (&wrq, 0, sizeof (wrq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	err = ioctl (wext->fd, SIOCGIWRATE, &wrq);
 	return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0);
 }
@@ -356,7 +357,7 @@ wifi_wext_get_qual (WifiData *data)
 	wrq.u.data.pointer = &stats;
 	wrq.u.data.length = sizeof (stats);
 	wrq.u.data.flags = 1;  /* Clear updated flag */
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 
 	if (ioctl (wext->fd, SIOCGIWSTATS, &wrq) < 0) {
 		nm_log_warn (LOGD_HW | LOGD_WIFI,
@@ -393,7 +394,7 @@ wifi_wext_set_mesh_channel (WifiData *data, guint32 channel)
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 
 	if (channel > 0) {
 		wrq.u.freq.flags = IW_FREQ_FIXED;
@@ -425,7 +426,7 @@ wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
 	wrq.u.essid.length = len;
 	wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */
 
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	if (ioctl (wext->fd, SIOCSIWESSID, &wrq) == 0)
 		return TRUE;
 
@@ -448,7 +449,7 @@ wext_can_scan (WifiDataWext *wext)
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	if (ioctl (wext->fd, SIOCSIWSCAN, &wrq) < 0) {
 		if (errno == EOPNOTSUPP)
 			return FALSE;
@@ -466,7 +467,7 @@ wext_get_range (WifiDataWext *wext,
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	strncpy (wrq.ifr_name, wext->parent.iface, IFNAMSIZ);
+	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
 	wrq.u.data.pointer = (caddr_t) range;
 	wrq.u.data.length = sizeof (struct iw_range);
 
@@ -666,7 +667,7 @@ wifi_wext_is_wifi (const char *iface)
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd >= 0) {
-		strncpy (iwr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
+		nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface);
 		if (ioctl (fd, SIOCGIWNAME, &iwr) == 0)
 			is_wifi = TRUE;
 		close (fd);
diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c
index 1578e595..16e34f11 100644
--- a/src/ppp-manager/nm-ppp-manager.c
+++ b/src/ppp-manager/nm-ppp-manager.c
@@ -49,6 +49,16 @@
 
 #include "nmdbus-ppp-manager.h"
 
+#define _NMLOG_DOMAIN         LOGD_PPP
+#define _NMLOG_PREFIX_NAME    "ppp-manager"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
 static void _ppp_cleanup  (NMPPPManager *manager);
 static void _ppp_kill (NMPPPManager *manager);
 
@@ -185,7 +195,7 @@ monitor_cb (gpointer user_data)
 	strncpy (req.ifr_name, priv->ip_iface, sizeof (req.ifr_name));
 	if (ioctl (priv->monitor_fd, SIOCGPPPSTATS, &req) < 0) {
 		if (errno != ENODEV)
-			nm_log_warn (LOGD_PPP, "could not read ppp stats: %s", strerror (errno));
+			_LOGW ("could not read ppp stats: %s", strerror (errno));
 	} else {
 		g_signal_emit (manager, signals[STATS], 0, 
 		               stats.p.ppp_ibytes,
@@ -211,7 +221,7 @@ monitor_stats (NMPPPManager *manager)
 			g_source_remove (priv->monitor_id);
 		priv->monitor_id = g_timeout_add_seconds (5, monitor_cb, manager);
 	} else
-		nm_log_warn (LOGD_PPP, "could not monitor PPP stats: %s", strerror (errno));
+		_LOGW ("could not monitor PPP stats: %s", strerror (errno));
 }
 
 /*******************************************/
@@ -316,7 +326,7 @@ ppp_secrets_cb (NMActRequest *req,
 		goto out;
 
 	if (error) {
-		nm_log_warn (LOGD_PPP, "%s", error->message);
+		_LOGW ("%s", error->message);
 		g_dbus_method_invocation_return_gerror (priv->pending_secrets_context, error);
 		goto out;
 	}
@@ -324,7 +334,7 @@ ppp_secrets_cb (NMActRequest *req,
 	applied_connection = nm_act_request_get_applied_connection (req);
 
 	if (!extract_details_from_connection (applied_connection, priv->secrets_setting_name, &username, &password, &local)) {
-		nm_log_warn (LOGD_PPP, "%s", local->message);
+		_LOGW ("%s", local->message);
 		g_dbus_method_invocation_take_error (priv->pending_secrets_context, local);
 		goto out;
 	}
@@ -370,7 +380,7 @@ impl_ppp_manager_need_secrets (NMPPPManager *manager,
 			priv->pending_secrets_context = context;
 			ppp_secrets_cb (priv->act_req, priv->secrets_id, NULL, NULL, manager);
 		} else {
-			nm_log_warn (LOGD_PPP, "%s", error->message);
+			_LOGW ("%s", error->message);
 			g_dbus_method_invocation_take_error (priv->pending_secrets_context, error);
 		}
 		return;
@@ -419,7 +429,7 @@ set_ip_config_common (NMPPPManager *self,
 	const char *iface;
 
 	if (!g_variant_lookup (config_dict, iface_prop, "&s", &iface)) {
-		nm_log_err (LOGD_PPP, "no interface received!");
+		_LOGE ("no interface received!");
 		return FALSE;
 	}
 	if (priv->ip_iface == NULL)
@@ -450,7 +460,7 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
 	guint32 u32;
 	GVariantIter *iter;
 
-	nm_log_info (LOGD_PPP, "PPP manager (IPv4 Config Get) reply received.");
+	_LOGI ("(IPv4 Config Get) reply received.");
 
 	remove_timeout_handler (manager);
 
@@ -475,7 +485,7 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager,
 		address.source = NM_IP_CONFIG_SOURCE_PPP;
 		nm_ip4_config_add_address (config, &address);
 	} else {
-		nm_log_err (LOGD_PPP, "invalid IPv4 address received!");
+		_LOGE ("invalid IPv4 address received!");
 		goto out;
 	}
 
@@ -517,7 +527,7 @@ iid_value_to_ll6_addr (GVariant *dict,
 	guint64 iid;
 
 	if (!g_variant_lookup (dict, prop, "t", &iid)) {
-		nm_log_dbg (LOGD_PPP, "pppd plugin property '%s' missing or not a uint64", prop);
+		_LOGD ("pppd plugin property '%s' missing or not a uint64", prop);
 		return FALSE;
 	}
 	g_return_val_if_fail (iid != 0, FALSE);
@@ -545,7 +555,7 @@ impl_ppp_manager_set_ip6_config (NMPPPManager *manager,
 	struct in6_addr a;
 	NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
 
-	nm_log_info (LOGD_PPP, "PPP manager (IPv6 Config Get) reply received.");
+	_LOGI ("(IPv6 Config Get) reply received.");
 
 	remove_timeout_handler (manager);
 
@@ -567,7 +577,7 @@ impl_ppp_manager_set_ip6_config (NMPPPManager *manager,
 			g_signal_emit (manager, signals[IP6_CONFIG], 0, priv->ip_iface, &iid, config);
 		}
 	} else
-		nm_log_err (LOGD_PPP, "invalid IPv6 address received!");
+		_LOGE ("invalid IPv6 address received!");
 
 	g_object_unref (config);
 	g_dbus_method_invocation_return_value (context, NULL);
@@ -771,7 +781,7 @@ ppp_exit_code (guint pppd_exit_status, GPid pid)
 		msg = "Unknown error";
 	}
 
-	nm_log_warn (LOGD_PPP, "pppd pid %d exited with error: %s", pid, msg);
+	_LOGW ("pppd pid %d exited with error: %s", pid, msg);
 }
 
 static void
@@ -788,13 +798,13 @@ ppp_watch_cb (GPid pid, gint status, gpointer user_data)
 		if (err != 0)
 			ppp_exit_code (err, priv->pid);
 	} else if (WIFSTOPPED (status)) {
-		nm_log_info (LOGD_PPP, "pppd pid %d stopped unexpectedly with signal %d", priv->pid, WSTOPSIG (status));
+		_LOGI ("pppd pid %d stopped unexpectedly with signal %d", priv->pid, WSTOPSIG (status));
 	} else if (WIFSIGNALED (status)) {
-		nm_log_info (LOGD_PPP, "pppd pid %d died with signal %d", priv->pid, WTERMSIG (status));
+		_LOGI ("pppd pid %d died with signal %d", priv->pid, WTERMSIG (status));
 	} else
-		nm_log_info (LOGD_PPP, "pppd pid %d died from an unknown cause", priv->pid);
+		_LOGI ("pppd pid %d died from an unknown cause", priv->pid);
 
-	nm_log_dbg (LOGD_PPP, "pppd pid %d cleaned up", priv->pid);
+	_LOGD ("pppd pid %d cleaned up", priv->pid);
 	priv->pid = 0;
 	priv->ppp_watch_id = 0;
 	g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_PPP_STATUS_DEAD);
@@ -805,7 +815,7 @@ pppd_timed_out (gpointer data)
 {
 	NMPPPManager *manager = NM_PPP_MANAGER (data);
 
-	nm_log_warn (LOGD_PPP, "pppd timed out or didn't initialize our dbus module");
+	_LOGW ("pppd timed out or didn't initialize our dbus module");
 	_ppp_cleanup (manager);
 	_ppp_kill (manager);
 
@@ -1065,10 +1075,10 @@ nm_ppp_manager_start (NMPPPManager *manager,
 
 	g_ptr_array_add (ppp_cmd->array, NULL);
 
-	nm_log_info (LOGD_PPP, "starting PPP connection");
+	_LOGI ("starting PPP connection");
 
 	cmd_str = nm_cmd_line_to_str (ppp_cmd);
-	nm_log_dbg (LOGD_PPP, "command line: %s", cmd_str);
+	_LOGD ("command line: %s", cmd_str);
 	g_free (cmd_str);
 
 	priv->pid = 0;
@@ -1079,7 +1089,7 @@ nm_ppp_manager_start (NMPPPManager *manager,
 		goto out;
 	}
 
-	nm_log_info (LOGD_PPP, "pppd started with pid %d", priv->pid);
+	_LOGI ("pppd started with pid %d", priv->pid);
 
 	priv->ppp_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) ppp_watch_cb, manager);
 	priv->ppp_timeout_handler = g_timeout_add_seconds (timeout_secs, pppd_timed_out, manager);
diff --git a/src/ppp-manager/nm-pppd-plugin.c b/src/ppp-manager/nm-pppd-plugin.c
index be4dd185..4c16f1f0 100644
--- a/src/ppp-manager/nm-pppd-plugin.c
+++ b/src/ppp-manager/nm-pppd-plugin.c
@@ -302,10 +302,9 @@ get_credentials (char *username, char *password)
 	                              G_DBUS_CALL_FLAGS_NONE, -1,
 	                              NULL, &err);
 	if (!ret) {
-		g_warning ("nm-ppp-plugin: (%s): could not get secrets: (%d) %s",
+		g_warning ("nm-ppp-plugin: (%s): could not get secrets: %s",
 		           __func__,
-		           err ? err->code : -1,
-		           err->message ? err->message : "(unknown)");
+		           err->message);
 		g_error_free (err);
 		return -1;
 	}
diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c
index 06863462..27fd2f83 100644
--- a/src/rdisc/nm-lndp-rdisc.c
+++ b/src/rdisc/nm-lndp-rdisc.c
@@ -31,6 +31,7 @@
 
 #include "NetworkManagerUtils.h"
 #include "nm-platform.h"
+#include "nmp-netns.h"
 
 #define _NMLOG_PREFIX_NAME                "rdisc-lndp"
 
@@ -273,9 +274,14 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 static gboolean
 event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
 
 	_LOGD ("processing libndp events");
+
+	if (!nm_rdisc_netns_push (rdisc, &netns))
+		return G_SOURCE_CONTINUE;
+
 	ndp_callall_eventfd_handler (priv->ndp);
 	return G_SOURCE_CONTINUE;
 }
@@ -298,40 +304,50 @@ start (NMRDisc *rdisc)
 /******************************************************************/
 
 static inline gint32
-ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval)
+ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, gint32 defval)
 {
-	return nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, nm_utils_ip6_property_path (ifname, property), defval);
+	return nm_platform_sysctl_get_int32 (platform, nm_utils_ip6_property_path (ifname, property), defval);
 }
 
 NMRDisc *
-nm_lndp_rdisc_new (int ifindex,
+nm_lndp_rdisc_new (NMPlatform *platform,
+                   int ifindex,
                    const char *ifname,
                    const char *uuid,
                    NMSettingIP6ConfigAddrGenMode addr_gen_mode,
                    GError **error)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMRDisc *rdisc;
 	NMLNDPRDiscPrivate *priv;
 	int errsv;
 
+	g_return_val_if_fail (NM_IS_PLATFORM (platform), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 
-	rdisc = g_object_new (NM_TYPE_LNDP_RDISC, NULL);
+	if (!nm_platform_netns_push (platform, &netns))
+		return NULL;
+
+	rdisc = g_object_new (NM_TYPE_LNDP_RDISC,
+	                      NM_RDISC_PLATFORM, platform,
+	                      NULL);
 
 	rdisc->ifindex = ifindex;
 	rdisc->ifname = g_strdup (ifname);
 	rdisc->uuid = g_strdup (uuid);
 	rdisc->addr_gen_mode = addr_gen_mode;
 
-	rdisc->max_addresses = ipv6_sysctl_get (ifname, "max_addresses",
+	rdisc->max_addresses = ipv6_sysctl_get (platform, ifname, "max_addresses",
 	                                        NM_RDISC_MAX_ADDRESSES_DEFAULT);
-	rdisc->rtr_solicitations = ipv6_sysctl_get (ifname, "router_solicitations",
+	rdisc->rtr_solicitations = ipv6_sysctl_get (platform, ifname, "router_solicitations",
 	                                            NM_RDISC_RTR_SOLICITATIONS_DEFAULT);
-	rdisc->rtr_solicitation_interval = ipv6_sysctl_get (ifname, "router_solicitation_interval",
+	rdisc->rtr_solicitation_interval = ipv6_sysctl_get (platform, ifname, "router_solicitation_interval",
 	                                                    NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT);
 
 	priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
+
 	errsv = ndp_open (&priv->ndp);
+
 	if (errsv != 0) {
 		errsv = errsv > 0 ? errsv : -errsv;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h
index e6706e3c..4c7c4743 100644
--- a/src/rdisc/nm-lndp-rdisc.h
+++ b/src/rdisc/nm-lndp-rdisc.h
@@ -44,7 +44,8 @@ typedef struct {
 
 GType nm_lndp_rdisc_get_type (void);
 
-NMRDisc *nm_lndp_rdisc_new (int ifindex,
+NMRDisc *nm_lndp_rdisc_new (NMPlatform *platform,
+                            int ifindex,
                             const char *ifname,
                             const char *uuid,
                             NMSettingIP6ConfigAddrGenMode addr_gen_mode,
diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c
index 82aece30..689fe7f6 100644
--- a/src/rdisc/nm-rdisc.c
+++ b/src/rdisc/nm-rdisc.c
@@ -28,6 +28,8 @@
 #include "nm-rdisc-private.h"
 
 #include "nm-utils.h"
+#include "nm-platform.h"
+#include "nmp-netns.h"
 
 #include <nm-setting-ip6-config.h>
 
@@ -46,6 +48,10 @@ typedef struct {
 
 G_DEFINE_TYPE (NMRDisc, nm_rdisc, G_TYPE_OBJECT)
 
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PLATFORM,
+);
+
 enum {
 	CONFIG_CHANGED,
 	RA_TIMEOUT,
@@ -56,6 +62,31 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 /******************************************************************/
 
+NMPNetns *
+nm_rdisc_netns_get (NMRDisc *self)
+{
+	g_return_val_if_fail (NM_IS_RDISC (self), NULL);
+
+	return self->_netns;
+}
+
+gboolean
+nm_rdisc_netns_push (NMRDisc *self, NMPNetns **netns)
+{
+	g_return_val_if_fail (NM_IS_RDISC (self), FALSE);
+
+	if (   self->_netns
+	    && !nmp_netns_push (self->_netns)) {
+		NM_SET_OUT (netns, NULL);
+		return FALSE;
+	}
+
+	NM_SET_OUT (netns, self->_netns);
+	return TRUE;
+}
+
+/******************************************************************/
+
 gboolean
 nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new)
 {
@@ -317,10 +348,14 @@ nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid)
 static gboolean
 send_rs (NMRDisc *rdisc)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMRDiscClass *klass = NM_RDISC_GET_CLASS (rdisc);
 	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
 	GError *error = NULL;
 
+	if (!nm_rdisc_netns_push (rdisc, &netns))
+		return G_SOURCE_REMOVE;
+
 	if (klass->send_rs (rdisc, &error)) {
 		_LOGD ("router solicitation sent");
 		priv->solicitations_left--;
@@ -383,6 +418,7 @@ rdisc_ra_timeout_cb (gpointer user_data)
 void
 nm_rdisc_start (NMRDisc *rdisc)
 {
+	nm_auto_pop_netns NMPNetns *netns = NULL;
 	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
 	NMRDiscClass *klass = NM_RDISC_GET_CLASS (rdisc);
 	guint ra_wait_secs;
@@ -391,6 +427,9 @@ nm_rdisc_start (NMRDisc *rdisc)
 
 	_LOGD ("starting router discovery: %d", rdisc->ifindex);
 
+	if (!nm_rdisc_netns_push (rdisc, &netns))
+		return;
+
 	nm_clear_g_source (&priv->ra_timeout_id);
 	ra_wait_secs = CLAMP (rdisc->rtr_solicitations * rdisc->rtr_solicitation_interval, 30, 120);
 	priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, rdisc_ra_timeout_cb, rdisc);
@@ -669,6 +708,33 @@ dns_domain_free (gpointer data)
 }
 
 static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMRDisc *self = NM_RDISC (object);
+
+	switch (prop_id) {
+	case PROP_PLATFORM:
+		/* construct-only */
+		self->_platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
+		if (!self->_platform)
+			g_return_if_reached ();
+
+		g_object_ref (self->_platform);
+
+		self->_netns = nm_platform_netns_get (self->_platform);
+		if (self->_netns)
+			g_object_ref (self->_netns);
+
+		g_return_if_fail (!self->_netns || self->_netns == nmp_netns_get_current ());
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
 nm_rdisc_init (NMRDisc *rdisc)
 {
 	NMRDiscPrivate *priv = NM_RDISC_GET_PRIVATE (rdisc);
@@ -715,6 +781,9 @@ finalize (GObject *object)
 	g_array_unref (rdisc->dns_servers);
 	g_array_unref (rdisc->dns_domains);
 
+	g_clear_object (&rdisc->_netns);
+	g_clear_object (&rdisc->_platform);
+
 	G_OBJECT_CLASS (nm_rdisc_parent_class)->finalize (object);
 }
 
@@ -725,23 +794,31 @@ nm_rdisc_class_init (NMRDiscClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMRDiscPrivate));
 
+	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 	klass->config_changed = config_changed;
 
-	signals[CONFIG_CHANGED] = g_signal_new (
-			NM_RDISC_CONFIG_CHANGED,
-			G_OBJECT_CLASS_TYPE (klass),
-			G_SIGNAL_RUN_FIRST,
-			G_STRUCT_OFFSET (NMRDiscClass, config_changed),
-			NULL, NULL, NULL,
-			G_TYPE_NONE, 1, G_TYPE_INT);
-
-	signals[RA_TIMEOUT] = g_signal_new (
-			NM_RDISC_RA_TIMEOUT,
-			G_OBJECT_CLASS_TYPE (klass),
-			G_SIGNAL_RUN_FIRST,
-			G_STRUCT_OFFSET (NMRDiscClass, ra_timeout),
-			NULL, NULL, NULL,
-			G_TYPE_NONE, 0);
+	obj_properties[PROP_PLATFORM] =
+	    g_param_spec_object (NM_RDISC_PLATFORM, "", "",
+	                         NM_TYPE_PLATFORM,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	signals[CONFIG_CHANGED] =
+	    g_signal_new (NM_RDISC_CONFIG_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (klass),
+	                  G_SIGNAL_RUN_FIRST,
+	                  G_STRUCT_OFFSET (NMRDiscClass, config_changed),
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 1, G_TYPE_INT);
+	signals[RA_TIMEOUT] =
+	    g_signal_new (NM_RDISC_RA_TIMEOUT,
+	                  G_OBJECT_CLASS_TYPE (klass),
+	                  G_SIGNAL_RUN_FIRST,
+	                  G_STRUCT_OFFSET (NMRDiscClass, ra_timeout),
+	                  NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 }
diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h
index 51e2602f..5b97bec5 100644
--- a/src/rdisc/nm-rdisc.h
+++ b/src/rdisc/nm-rdisc.h
@@ -21,11 +21,9 @@
 #ifndef __NETWORKMANAGER_RDISC_H__
 #define __NETWORKMANAGER_RDISC_H__
 
-
 #include <stdlib.h>
 #include <netinet/in.h>
 
-#include "nm-default.h"
 #include "nm-setting-ip6-config.h"
 #include "NetworkManagerUtils.h"
 
@@ -36,6 +34,7 @@
 #define NM_IS_RDISC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_RDISC))
 #define NM_RDISC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_RDISC, NMRDiscClass))
 
+#define NM_RDISC_PLATFORM       "platform"
 #define NM_RDISC_CONFIG_CHANGED "config-changed"
 #define NM_RDISC_RA_TIMEOUT     "ra-timeout"
 
@@ -114,6 +113,9 @@ typedef enum {
 typedef struct {
 	GObject parent;
 
+	NMPlatform *_platform;
+	NMPNetns *_netns;
+
 	int ifindex;
 	char *ifname;
 	char *uuid;
@@ -149,4 +151,8 @@ gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid);
 void nm_rdisc_start (NMRDisc *rdisc);
 void nm_rdisc_dad_failed (NMRDisc *rdisc, struct in6_addr *address);
 
+NMPlatform *nm_rdisc_get_platform (NMRDisc *self);
+NMPNetns *nm_rdisc_netns_get (NMRDisc *self);
+gboolean nm_rdisc_netns_push (NMRDisc *self, NMPNetns **netns);
+
 #endif /* __NETWORKMANAGER_RDISC_H__ */
diff --git a/src/rdisc/tests/test-rdisc-fake.c b/src/rdisc/tests/test-rdisc-fake.c
index 00a98dca..1c514b90 100644
--- a/src/rdisc/tests/test-rdisc-fake.c
+++ b/src/rdisc/tests/test-rdisc-fake.c
@@ -431,7 +431,7 @@ main (int argc, char **argv)
 	nmtst_init_with_logging (&argc, &argv, NULL, "DEFAULT");
 
 	if (nmtst_test_quick ()) {
-		g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", str_if_set (g_get_prgname (), "test-rdisc-fake"));
+		g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", g_get_prgname () ?: "test-rdisc-fake");
 		return g_test_run ();
 	}
 
diff --git a/src/rdisc/tests/test-rdisc-linux.c b/src/rdisc/tests/test-rdisc-linux.c
index bbdb5fb8..e22eb1ad 100644
--- a/src/rdisc/tests/test-rdisc-linux.c
+++ b/src/rdisc/tests/test-rdisc-linux.c
@@ -61,7 +61,8 @@ main (int argc, char **argv)
 		return EXIT_FAILURE;
 	}
 
-	rdisc = nm_lndp_rdisc_new (ifindex,
+	rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET,
+	                           ifindex,
 	                           ifname,
 	                           "8ce666e8-d34d-4fb1-b858-f15a7al28086",
 	                           NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index bc09e031..48a7da7b 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -263,8 +263,8 @@ agent_register_permissions_done (NMAuthChain *chain,
 	if (error) {
 		local = g_error_new (NM_AGENT_MANAGER_ERROR,
 		                     NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
-		                     "Failed to request agent permissions: (%d) %s",
-		                     error->code, error->message);
+		                     "Failed to request agent permissions: %s",
+		                     error->message);
 		g_dbus_method_invocation_take_error (context, local);
 	} else {
 		agent = nm_auth_chain_steal_data (chain, "agent");
@@ -930,6 +930,7 @@ set_secrets_not_required (NMConnection *connection, GVariant *dict)
 				g_variant_unref (val);
 			}
 		}
+		g_variant_unref (setting_dict);
 	}
 }
 
@@ -987,9 +988,9 @@ _con_get_request_start_validated (NMAuthChain *chain,
 	req->con.chain = NULL;
 
 	if (error) {
-		_LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check error: (%d) %s",
+		_LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check error: %s",
 		       LOG_REQ_ARG (req),
-		       error->code, error->message ? error->message : "(unknown)");
+		       error->message);
 		/* Try the next agent */
 		request_next_agent (req);
 	} else {
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index a1695b34..6bed3fe5 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -883,10 +883,8 @@ new_secrets_commit_cb (NMSettingsConnection *self,
                        GError *error,
                        gpointer user_data)
 {
-	if (error) {
-		_LOGW ("Error saving new secrets to backing storage: (%d) %s",
-		       error->code, error->message ? error->message : "(unknown)");
-	}
+	if (error)
+		_LOGW ("Error saving new secrets to backing storage: %s", error->message);
 }
 
 static void
@@ -1086,19 +1084,17 @@ get_secrets_done_cb (NMAgentManager *manager,
 			}
 
 		} else {
-			_LOGD ("(%s:%p) failed to update with agent secrets: (%d) %s",
+			_LOGD ("(%s:%p) failed to update with agent secrets: %s",
 			       setting_name,
 			       info,
-			       local ? local->code : -1,
-			       (local && local->message) ? local->message : "(unknown)");
+			       local->message);
 		}
 		g_variant_unref (filtered_secrets);
 	} else {
-		_LOGD ("(%s:%p) failed to update with existing secrets: (%d) %s",
+		_LOGD ("(%s:%p) failed to update with existing secrets: %s",
 		       setting_name,
 		       info,
-		       local ? local->code : -1,
-		       (local && local->message) ? local->message : "(unknown)");
+		       local->message);
 	}
 
 	applied_connection = info->applied_connection;
@@ -1739,7 +1735,10 @@ settings_connection_update_helper (NMSettingsConnection *self,
 
 	/* Check if the settings are valid first */
 	if (new_settings) {
-		tmp = nm_simple_connection_new_from_dbus (new_settings, &error);
+		tmp = _nm_simple_connection_new_from_dbus (new_settings,
+		                                             NM_SETTING_PARSE_FLAGS_STRICT
+		                                           | NM_SETTING_PARSE_FLAGS_NORMALIZE,
+		                                           &error);
 		if (!tmp)
 			goto error;
 	}
@@ -2163,7 +2162,7 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *self,
 	/* Save timestamp to timestamps database file */
 	timestamps_file = g_key_file_new ();
 	if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
-		if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT))
+		if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
 			_LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
 		g_clear_error (&error);
 	}
@@ -2172,7 +2171,7 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *self,
 	tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
 	g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
 	g_free (tmp);
- 
+
 	data = g_key_file_to_data (timestamps_file, &len, &error);
 	if (data) {
 		g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
@@ -2219,8 +2218,7 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
 		priv->timestamp = timestamp;
 		priv->timestamp_set = TRUE;
 	} else {
-		_LOGD ("failed to read connection timestamp: (%d) %s",
-		       err->code, err->message);
+		_LOGD ("failed to read connection timestamp: %s", err->message);
 		g_clear_error (&err);
 	}
 	g_key_file_free (timestamps_file);
diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c
index 2023bc17..7de7e597 100644
--- a/src/settings/nm-settings-plugin.c
+++ b/src/settings/nm-settings-plugin.c
@@ -175,5 +175,7 @@ nm_settings_plugin_add_connection (NMSettingsPlugin *config,
 	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->add_connection)
 		return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->add_connection (config, connection, save_to_disk, error);
 
+	g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
+	                     "Plugin does not support adding connections");
 	return NULL;
 }
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index ee48e711..77e45f49 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -76,12 +76,15 @@
 
 #include "nmdbus-settings.h"
 
-#define LOG(level, ...) \
-	G_STMT_START { \
-		nm_log ((level), LOGD_CORE, \
-		        "settings: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__) \
-		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
-	} G_STMT_END
+#define _NMLOG_DOMAIN         LOGD_SETTINGS
+#define _NMLOG_PREFIX_NAME    "settings"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
 
 /* LINKER CRACKROCK */
 #define EXPORT(sym) void * __export_##sym = &sym;
@@ -652,8 +655,8 @@ add_plugin (NMSettings *self, NMSettingsPlugin *plugin)
 
 	path = g_object_get_data (G_OBJECT (plugin), PLUGIN_MODULE_PATH);
 
-	nm_log_info (LOGD_SETTINGS, "Loaded settings plugin %s: %s%s%s%s", pname, pinfo,
-	             NM_PRINT_FMT_QUOTED (path, " (", path, ")", ""));
+	_LOGI ("loaded plugin %s: %s%s%s%s", pname, pinfo,
+	       NM_PRINT_FMT_QUOTED (path, " (", path, ")", ""));
 	g_free (pname);
 	g_free (pinfo);
 
@@ -719,12 +722,12 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 		GObject *obj;
 
 		if (!*pname || strchr (pname, '/')) {
-			LOG (LOGL_WARN, "ignore invalid plugin \"%s\"", pname);
+			_LOGW ("ignore invalid plugin \"%s\"", pname);
 			continue;
 		}
 
 		if (!strcmp (pname, "ifcfg-suse")) {
-			LOG (LOGL_WARN, "skipping deprecated plugin ifcfg-suse");
+			_LOGW ("skipping deprecated plugin ifcfg-suse");
 			continue;
 		}
 
@@ -767,25 +770,25 @@ load_plugin:
 
 			if (stat (path, &st) != 0) {
 				errsv = errno;
-				LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv));
+				_LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv));
 				goto next;
 			}
 			if (!S_ISREG (st.st_mode)) {
-				LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': not a file", pname, path);
+				_LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path);
 				goto next;
 			}
 			if (st.st_uid != 0) {
-				LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': file must be owned by root", pname, path);
+				_LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path);
 				goto next;
 			}
 			if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
-				LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': invalid file permissions", pname, path);
+				_LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path);
 				goto next;
 			}
 
 			plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
 			if (!plugin) {
-				LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s",
+				_LOGW ("could not load plugin '%s' from file '%s': %s",
 				     pname, path, g_module_error ());
 				goto next;
 			}
@@ -985,8 +988,7 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	}
 
 	if (!nm_connection_normalize (NM_CONNECTION (connection), NULL, NULL, &error)) {
-		nm_log_warn (LOGD_SETTINGS, "plugin provided invalid connection: %s",
-		             error->message);
+		_LOGW ("plugin provided invalid connection: %s", error->message);
 		g_error_free (error);
 		return;
 	}
@@ -1003,8 +1005,8 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 		 * without the individual plugins being aware. Don't handle that at all, just
 		 * error out. That should not happen unless the admin misconfigured the system
 		 * to create conflicting connections. */
-		nm_log_warn (LOGD_SETTINGS, "plugin provided duplicate connection with UUID %s",
-		             nm_settings_connection_get_uuid (connection));
+		_LOGW ("plugin provided duplicate connection with UUID %s",
+		       nm_settings_connection_get_uuid (connection));
 		return;
 	}
 
@@ -1117,10 +1119,10 @@ nm_settings_add_connection (NMSettings *self,
 			claim_connection (self, added);
 			return added;
 		}
-		nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s",
-		            nm_connection_get_uuid (connection),
-		            nm_connection_get_id (connection),
-		            add_error ? add_error->message : "(unknown)");
+		_LOGD ("Failed to add %s/'%s': %s",
+		       nm_connection_get_uuid (connection),
+		       nm_connection_get_id (connection),
+		       add_error->message);
 		g_clear_error (&add_error);
 	}
 
@@ -1211,7 +1213,7 @@ pk_add_cb (NMAuthChain *chain,
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_FAILED,
 		                     "Error checking authorization: %s",
-		                     chain_error->message ? chain_error->message : "(unknown)");
+		                     chain_error->message);
 	} else if (result != NM_AUTH_CALL_RESULT_YES) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -1294,7 +1296,7 @@ nm_settings_add_connection_dbus (NMSettings *self,
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "The connection was invalid: %s",
-		                     tmp_error ? tmp_error->message : "(unknown)");
+		                     tmp_error->message);
 		g_error_free (tmp_error);
 		goto done;
 	}
@@ -1405,7 +1407,10 @@ impl_settings_add_connection_helper (NMSettings *self,
 	NMConnection *connection;
 	GError *error = NULL;
 
-	connection = nm_simple_connection_new_from_dbus (settings, &error);
+	connection = _nm_simple_connection_new_from_dbus (settings,
+	                                                    NM_SETTING_PARSE_FLAGS_STRICT
+	                                                  | NM_SETTING_PARSE_FLAGS_NORMALIZE,
+	                                                  &error);
 
 	if (connection) {
 		if (!nm_connection_verify_secrets (connection, &error))
@@ -1492,7 +1497,7 @@ impl_settings_load_connections (NMSettings *self,
 
 		if (!iter) {
 			if (!g_path_is_absolute (filenames[i]))
-				nm_log_warn (LOGD_SETTINGS, "Connection filename '%s' is not an absolute path", filenames[i]);
+				_LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
 			g_ptr_array_add (failures, (char *) filenames[i]);
 		}
 	}
@@ -1525,6 +1530,65 @@ impl_settings_reload_connections (NMSettings *self,
 	g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE));
 }
 
+typedef struct {
+	char *hostname;
+	NMSettingsSetHostnameCb cb;
+	gpointer user_data;
+} SetHostnameInfo;
+
+static void
+set_transient_hostname_done (GObject *object,
+                             GAsyncResult *res,
+                             gpointer user_data)
+{
+	GDBusProxy *proxy = G_DBUS_PROXY (object);
+	gs_free SetHostnameInfo *info = user_data;
+	gs_unref_variant GVariant *result = NULL;
+	gs_free_error GError *error = NULL;
+
+	result = g_dbus_proxy_call_finish (proxy, res, &error);
+
+	if (error) {
+		_LOGW ("couldn't set the system hostname to '%s' using hostnamed: %s",
+		       info->hostname, error->message);
+	}
+
+	info->cb (info->hostname, !error, info->user_data);
+	g_free (info->hostname);
+}
+
+void
+nm_settings_set_transient_hostname (NMSettings *self,
+                                    const char *hostname,
+                                    NMSettingsSetHostnameCb cb,
+                                    gpointer user_data)
+{
+	NMSettingsPrivate *priv;
+	SetHostnameInfo *info;
+
+	g_return_if_fail (NM_IS_SETTINGS (self));
+	priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	if (!priv->hostname.hostnamed_proxy) {
+		cb (hostname, FALSE, user_data);
+		return;
+	}
+
+	info = g_new0 (SetHostnameInfo, 1);
+	info->hostname = g_strdup (hostname);
+	info->cb = cb;
+	info->user_data = user_data;
+
+	g_dbus_proxy_call (priv->hostname.hostnamed_proxy,
+	                   "SetHostname",
+	                   g_variant_new ("(sb)", hostname, FALSE),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   NULL,
+	                   set_transient_hostname_done,
+	                   info);
+}
+
 static gboolean
 write_hostname (NMSettingsPrivate *priv, const char *hostname)
 {
@@ -1549,7 +1613,7 @@ write_hostname (NMSettingsPrivate *priv, const char *hostname)
 		                              NULL,
 		                              &error);
 		if (error)
-			nm_log_warn (LOGD_SETTINGS, "Could not set hostname: %s", error->message);
+			_LOGW ("could not set hostname: %s", error->message);
 
 		return !error;
 	}
@@ -1595,7 +1659,7 @@ write_hostname (NMSettingsPrivate *priv, const char *hostname)
 	g_free (hostname_eol);
 
 	if (!ret) {
-		nm_log_warn (LOGD_SETTINGS, "Could not save hostname to %s: %s", file, error->message);
+		_LOGW ("could not save hostname to %s: %s", file, error->message);
 		return FALSE;
 	}
 
@@ -1625,7 +1689,7 @@ pk_hostname_cb (NMAuthChain *chain,
 		error = g_error_new (NM_SETTINGS_ERROR,
 		                     NM_SETTINGS_ERROR_FAILED,
 		                     "Error checking authorization: %s",
-		                     chain_error->message ? chain_error->message : "(unknown)");
+		                     chain_error->message);
 	} else if (result != NM_AUTH_CALL_RESULT_YES) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -1721,9 +1785,9 @@ hostname_maybe_changed (NMSettings *settings)
 	    || (!new_hostname && priv->hostname.value)
 	    || (priv->hostname.value && new_hostname && strcmp (priv->hostname.value, new_hostname))) {
 
-		nm_log_info (LOGD_SETTINGS, "hostname changed from %s%s%s to %s%s%s",
-		             NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
-		             NM_PRINT_FMT_QUOTED (new_hostname, "\"", new_hostname, "\"", "(none)"));
+		_LOGI ("hostname changed from %s%s%s to %s%s%s",
+		       NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
+		       NM_PRINT_FMT_QUOTED (new_hostname, "\"", new_hostname, "\"", "(none)"));
 		g_free (priv->hostname.value);
 		priv->hostname.value = new_hostname;
 		g_object_notify (G_OBJECT (settings), NM_SETTINGS_HOSTNAME);
@@ -1894,9 +1958,9 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 	g_object_unref (connection);
 
 	if (!added) {
-		nm_log_warn (LOGD_SETTINGS, "(%s) couldn't create default wired connection: %s",
-		             nm_device_get_iface (device),
-		             (error && error->message) ? error->message : "(unknown)");
+		_LOGW ("(%s) couldn't create default wired connection: %s",
+		       nm_device_get_iface (device),
+		       error->message);
 		g_clear_error (&error);
 		return;
 	}
@@ -1909,9 +1973,9 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 	g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
 	                  G_CALLBACK (default_wired_connection_removed_cb), self);
 
-	nm_log_info (LOGD_SETTINGS, "(%s): created default wired connection '%s'",
-	             nm_device_get_iface (device),
-	             nm_settings_connection_get_id (added));
+	_LOGI ("(%s): created default wired connection '%s'",
+	       nm_device_get_iface (device),
+	       nm_settings_connection_get_id (added));
 }
 
 void
@@ -2080,9 +2144,9 @@ hostnamed_properties_changed (GDBusProxy *proxy,
 	hostname = g_variant_get_string (v_hostname, NULL);
 
 	if (g_strcmp0 (priv->hostname.value, hostname) != 0) {
-		nm_log_info (LOGD_SETTINGS, "hostname changed from %s%s%s to %s%s%s",
-		             NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
-		             NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)"));
+		_LOGI ("hostname changed from %s%s%s to %s%s%s",
+		       NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
+		       NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)"));
 		g_free (priv->hostname.value);
 		priv->hostname.value = g_strdup (hostname);
 		g_object_notify (G_OBJECT (user_data), NM_SETTINGS_HOSTNAME);
@@ -2170,19 +2234,19 @@ nm_settings_start (NMSettings *self, GError **error)
 	if (proxy) {
 		variant = g_dbus_proxy_get_cached_property (proxy, "StaticHostname");
 		if (variant) {
-			nm_log_info (LOGD_SETTINGS, "hostname: using hostnamed");
+			_LOGI ("hostname: using hostnamed");
 			priv->hostname.hostnamed_proxy = proxy;
 			g_signal_connect (proxy, "g-properties-changed",
 			                  G_CALLBACK (hostnamed_properties_changed), self);
 			hostnamed_properties_changed (proxy, NULL, NULL, self);
 			g_variant_unref (variant);
 		} else {
-			nm_log_info (LOGD_SETTINGS, "hostname: couldn't get property from hostnamed");
+			_LOGI ("hostname: couldn't get property from hostnamed");
 			g_object_unref (proxy);
 		}
 	} else {
-		nm_log_info (LOGD_SETTINGS, "hostname: hostnamed not used as proxy creation failed with: %s",
-		             local_error->message);
+		_LOGI ("hostname: hostnamed not used as proxy creation failed with: %s",
+		       local_error->message);
 		g_clear_error (&local_error);
 	}
 
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 923b164b..051c2094 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -71,6 +71,8 @@ typedef struct {
 	void (*agent_registered) (NMSettings *self, NMSecretAgent *agent);
 } NMSettingsClass;
 
+typedef void (*NMSettingsSetHostnameCb) (const char *name, gboolean result, gpointer user_data);
+
 GType nm_settings_get_type (void);
 
 NMSettings *nm_settings_new (void);
@@ -127,4 +129,9 @@ gint nm_settings_sort_connections (gconstpointer a, gconstpointer b);
 
 gboolean nm_settings_get_startup_complete (NMSettings *self);
 
+void nm_settings_set_transient_hostname (NMSettings *self,
+                                         const char *hostname,
+                                         NMSettingsSetHostnameCb cb,
+                                         gpointer user_data);
+
 #endif  /* __NM_SETTINGS_H__ */
diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c
index a9085478..9c98d891 100644
--- a/src/settings/plugins/ibft/tests/test-ibft.c
+++ b/src/settings/plugins/ibft/tests/test-ibft.c
@@ -181,7 +181,7 @@ test_read_ibft_malformed (gconstpointer user_data)
 
 	g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*malformed iscsiadm record*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*malformed iscsiadm record*");
 
 	success = read_ibft_blocks (iscsiadm_path, &blocks, &error);
 	g_assert_no_error (error);
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml b/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml
index 1f308dd4..c2a6cb34 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-rh.xml
@@ -2,28 +2,28 @@
 
 <node name="/" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
     <interface name="com.redhat.ifcfgrh1">
-      <tp:docstring>
+      <annotation name="org.gtk.GDBus.DocString" value="
           Utility methods for handling NM integration with standard Red Hat ifcfg files.
-      </tp:docstring>
+      " />
 
       <method name="GetIfcfgDetails">
-        <tp:docstring>
+        <annotation name="org.gtk.GDBus.DocString" value="
           Given an ifcfg file, return various internal information about it.
-        </tp:docstring>
+        " />
         <arg name="ifcfg" type="s" direction="in">
-          <tp:docstring>
+          <annotation name="org.gtk.GDBus.DocString" value="
             The full path to an ifcfg file.
-          </tp:docstring>
+          " />
         </arg>
         <arg name="uuid" type="s" direction="out">
-          <tp:docstring>
+          <annotation name="org.gtk.GDBus.DocString" value="
             The UUID of the NM connection backed by this ifcfg file.  If the ifcfg file does not contain a UUID tag, this UUID is generated by NM, otherwise the UUID from the ifcfg file is used.
-          </tp:docstring>
+          " />
         </arg>
         <arg name="path" type="o" direction="out">
-          <tp:docstring>
+          <annotation name="org.gtk.GDBus.DocString" value="
             The object path of the NM connected backed by this ifcfg file.
-          </tp:docstring>
+          " />
         </arg>
       </method>
     </interface>
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index d28e3bf6..1fb6b789 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -207,8 +207,10 @@ update_connection (SettingsPluginIfcfg *self,
 		    && !protect_existing_connection
 		    && (!protected_connections || !g_hash_table_contains (protected_connections, connection)))
 			remove_connection (self, connection);
-		if (!source && !ignore_error)
-			_LOGW ("loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)");
+		if (!source) {
+			_NMLOG (ignore_error ? LOGL_DEBUG : LOGL_WARN,
+			        "loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)");
+		}
 		g_propagate_error (error, local);
 		return NULL;
 	}
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index ca512fc6..41ffa27d 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -3076,6 +3076,13 @@ fill_8021x (shvarFile *ifcfg,
 	read_8021x_list_value (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES",
 	                       s_8021x, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
 
+	value = svGetValue (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH", FALSE);
+	g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, value, NULL);
+	g_free (value);
+	value = svGetValue (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH", FALSE);
+	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, value, NULL);
+	g_free (value);
+
 	if (list)
 		g_strfreev (list);
 	if (keys)
@@ -4993,14 +5000,24 @@ connection_from_file_full (const char *filename,
 		type = svGetValue (parsed, "TYPE", FALSE);
 
 	if (!type) {
+		gs_free char *tmp = NULL;
 		char *device;
 
+		if ((tmp = svGetValue (parsed, "IPV6TUNNELIPV4", FALSE))) {
+			if (out_ignore_error)
+				*out_ignore_error = TRUE;
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Ignoring unsupported connection due to IPV6TUNNELIPV4");
+			goto done;
+		}
+
 		device = svGetValue (parsed, "DEVICE", FALSE);
 		if (!device) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "File '%s' had neither TYPE nor DEVICE keys.", filename);
 			goto done;
 		}
+		g_assert (device[0]);
 
 		if (!strcmp (device, "lo")) {
 			if (out_ignore_error)
@@ -5018,8 +5035,51 @@ connection_from_file_full (const char *filename,
 				type = g_strdup (TYPE_VLAN);
 			else if (is_wifi_device (device, parsed))
 				type = g_strdup (TYPE_WIRELESS);
-			else
-				type = g_strdup (TYPE_ETHERNET);
+			else {
+				gs_free char *p_path = NULL;
+				char *p_device;
+				gsize i;
+
+				/* network-functions detects DEVICETYPE based on the ifcfg-* name and the existence
+				 * of a ifup script:
+				 *    [ -z "$DEVICETYPE" ] && DEVICETYPE=$(echo ${DEVICE} | sed "s/[0-9]*$//")
+				 * later...
+				 *    OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${DEVICETYPE}"
+				 * */
+#define IFUP_PATH_PREFIX "/etc/sysconfig/network-scripts/ifup-"
+				i = strlen (device);
+				p_path = g_malloc (NM_STRLEN (IFUP_PATH_PREFIX) + i + 1);
+				p_device = &p_path[NM_STRLEN (IFUP_PATH_PREFIX)];
+				memcpy (p_device, device, i + 1);
+
+				/* strip trailing numbers */
+				while (i >= 1) {
+					i--;
+					if (p_device[i] < '0' || p_device[i] > '9')
+						break;
+					p_device[i] = '\0';
+				}
+
+				if (nm_streq (p_device, "eth"))
+					type = g_strdup (TYPE_ETHERNET);
+				else if (nm_streq (p_device, "wireless"))
+					type = g_strdup (TYPE_WIRELESS);
+				else if (p_device[0]) {
+					memcpy (p_path, IFUP_PATH_PREFIX, NM_STRLEN (IFUP_PATH_PREFIX));
+					if (access (p_path, X_OK) == 0) {
+						/* for all other types, this is not something we want to handle. */
+						if (out_ignore_error)
+							*out_ignore_error = TRUE;
+						g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+						             "Ignore script for unknown device type which has a matching %s script",
+						             p_path);
+						goto done;
+					}
+				}
+
+				if (!type)
+					type = g_strdup (TYPE_ETHERNET);
+			}
 		} else {
 			/* For the unit tests, there won't necessarily be any
 			 * adapters of the connection's type in the system so the
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am
index 36e52af4..fc759e6d 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am
@@ -77,6 +77,7 @@ EXTRA_DIST = \
 	ifcfg-test-ibft \
 	ifcfg-test-static-routes-legacy \
 	route-test-static-routes-legacy \
+	ifcfg-test-sit-ignore \
 	ifcfg-test-wired-static-routes \
 	route-test-wired-static-routes \
 	ifcfg-test-wired-static-routes-legacy \
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
index 2152961c..e98ea93c 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
@@ -473,6 +473,7 @@ EXTRA_DIST = \
 	ifcfg-test-ibft \
 	ifcfg-test-static-routes-legacy \
 	route-test-static-routes-legacy \
+	ifcfg-test-sit-ignore \
 	ifcfg-test-wired-static-routes \
 	route-test-wired-static-routes \
 	ifcfg-test-wired-static-routes-legacy \
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore
new file mode 100644
index 00000000..a2581db6
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore
@@ -0,0 +1,12 @@
+# this ifcfg-file represents a "sit" type without explicit TYPE.
+# Such connection types are not supported by NetworkManager and
+# the connection should be ignored based on the presence of
+# IPV6TUNNELIPV4.
+
+DEVICE=sit1
+BOOTPROTO=none
+ONBOOT=yes
+IPV6INIT=yes
+IPV6TUNNELIPV4=5.4.3.6
+IPV6TUNNELIPV4LOCAL=172.17.1.9
+IPV6ADDR=2001:470:2:3:4::2/64
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 8ca5c8bb..5ed25fda 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -310,7 +310,7 @@ test_read_miscellaneous_variables (void)
 	int mac_blacklist_num, i;
 	guint64 expected_timestamp = 0;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*invalid MAC in HWADDR_BLACKLIST 'XX:aa:invalid'*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-misc-variables",
 	                                    NULL, TYPE_ETHERNET, NULL);
@@ -563,7 +563,7 @@ test_read_wired_static_no_prefix (gconstpointer user_data)
 	file = g_strdup_printf (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-static-no-prefix-%u", expected_prefix);
 	expected_id = g_strdup_printf ("System test-wired-static-no-prefix-%u", expected_prefix);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*missing PREFIX, assuming*");
 	connection = _connection_from_file (file, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
@@ -785,7 +785,7 @@ test_read_wired_global_gateway_ignore (void)
 	NMSettingIPConfig *s_ip4;
 	char *unmanaged = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring GATEWAY (/etc/sysconfig/network) for * because the connection has no static addresses");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-global-gateway-ignore",
 	                                    TEST_IFCFG_DIR"/network-scripts/network-test-wired-global-gateway-ignore",
@@ -1120,7 +1120,7 @@ test_read_wired_ipv6_manual (void)
 	NMIPAddress *ip6_addr;
 	NMIPRoute *ip6_route;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*ignoring manual default route*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-manual",
 	                                    NULL, TYPE_ETHERNET, &unmanaged);
@@ -1492,7 +1492,7 @@ test_read_write_802_1X_subj_matches (void)
 	NMSetting8021x *s_8021x;
 	char *written = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*missing IEEE_8021X_CA_CERT*peap*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-802-1X-subj-matches",
 	                                    NULL, TYPE_ETHERNET, NULL);
@@ -1518,7 +1518,7 @@ test_read_write_802_1X_subj_matches (void)
 	                        TEST_SCRATCH_DIR "/network-scripts/",
 	                        &written);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*missing IEEE_8021X_CA_CERT*peap*");
 	reread = _connection_from_file (written, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
@@ -1683,7 +1683,7 @@ test_read_wired_aliases_bad (const char *base, const char *expected_id)
 static void
 test_read_wired_aliases_bad_1 (void)
 {
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*aliasem1:1*has no DEVICE*");
 	test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem1", "System aliasem1");
 }
@@ -1691,7 +1691,7 @@ test_read_wired_aliases_bad_1 (void)
 static void
 test_read_wired_aliases_bad_2 (void)
 {
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*aliasem2:1*has invalid DEVICE*");
 	test_read_wired_aliases_bad (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem2", "System aliasem2");
 }
@@ -3560,9 +3560,6 @@ test_write_wired_static (void)
 	                        TEST_SCRATCH_DIR "/network-scripts/",
 	                        &testfile);
 
-	/* reread will be normalized, so we must normalize connection too. */
-	nm_connection_normalize (connection, NULL, NULL, NULL);
-
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 	unlink (testfile);
 
@@ -7329,7 +7326,7 @@ test_read_vlan_reorder_hdr_1 (void)
 	NMConnection *connection;
 	NMSettingVlan *s_vlan;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*REORDER_HDR key is deprecated, use VLAN_FLAGS*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-vlan-reorder-hdr-1",
 	                                        NULL, TYPE_ETHERNET, NULL);
@@ -8167,7 +8164,7 @@ test_read_dcb_bad_booleans (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*invalid DCB_PG_STRICT value*not all 0s and 1s*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-booleans",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8182,7 +8179,7 @@ test_read_dcb_short_booleans (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*DCB_PG_STRICT value*8 characters*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-booleans",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8197,7 +8194,7 @@ test_read_dcb_bad_uints (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*invalid DCB_PG_UP2TC value*not 0 - 7*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-uints",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8212,7 +8209,7 @@ test_read_dcb_short_uints (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*DCB_PG_UP2TC value*8 characters*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-uints",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8227,7 +8224,7 @@ test_read_dcb_bad_percent (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*invalid DCB_PG_PCT percentage value*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-bad-percent",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8242,7 +8239,7 @@ test_read_dcb_short_percent (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*invalid DCB_PG_PCT percentage list value*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-short-percent",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8257,7 +8254,7 @@ test_read_dcb_pgpct_not_100 (void)
 {
 	gs_free_error GError *error = NULL;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*DCB_PG_PCT percentages do not equal 100*");
 	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dcb-pgpct-not-100",
 	                            NULL, TYPE_ETHERNET, &error);
@@ -8738,6 +8735,20 @@ test_read_vlan_trailing_spaces (void)
 	g_object_unref (connection);
 }
 
+/*****************************************************************************/
+
+static void
+test_sit_read_ignore (void)
+{
+	gs_free_error GError *error = NULL;
+
+	_connection_from_file_fail (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-sit-ignore",
+	                            NULL, TYPE_ETHERNET, &error);
+	nmtst_assert_error (error, 0, 0, "*Ignoring unsupported connection due to IPV6TUNNELIPV4*");
+}
+
+/*****************************************************************************/
+
 
 #define TPATH "/settings/plugins/ifcfg-rh/"
 
@@ -8937,10 +8948,8 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "wired/write-missing-ipv6", test_write_ethernet_missing_ipv6);
 	g_test_add_func (TPATH "write-dns-options", test_write_dns_options);
 
-	/* iSCSI / ibft */
 	g_test_add_func (TPATH "ibft/ignored", test_read_ibft_ignored);
 
-	/* Data Center Bridging (DCB) */
 	g_test_add_func (TPATH "dcb/read-basic", test_read_dcb_basic);
 	g_test_add_func (TPATH "dcb/write-basic", test_write_dcb_basic);
 	g_test_add_func (TPATH "dcb/default-app-priorities", test_read_dcb_default_app_priorities);
@@ -8956,7 +8965,6 @@ int main (int argc, char **argv)
 	g_test_add_data_func (TPATH "fcoe/write-fabric", (gpointer) NM_SETTING_DCB_FCOE_MODE_FABRIC, test_write_fcoe_mode);
 	g_test_add_data_func (TPATH "fcoe/write-vn2vn", (gpointer) NM_SETTING_DCB_FCOE_MODE_VN2VN, test_write_fcoe_mode);
 
-	/* bonding */
 	g_test_add_func (TPATH "bond/read-master", test_read_bond_main);
 	g_test_add_func (TPATH "bond/read-slave", test_read_bond_slave);
 	g_test_add_func (TPATH "bond/read-slave-ib", test_read_bond_slave_ib);
@@ -8965,20 +8973,20 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "bond/write-slave-ib", test_write_bond_slave_ib);
 	g_test_add_func (TPATH "bond/bonding-opts-numeric-mode", test_read_bond_opts_mode_numeric);
 
-	/* bridging */
 	g_test_add_func (TPATH "bridge/read-master", test_read_bridge_main);
 	g_test_add_func (TPATH "bridge/write-master", test_write_bridge_main);
 	g_test_add_func (TPATH "bridge/read-component", test_read_bridge_component);
 	g_test_add_func (TPATH "bridge/write-component", test_write_bridge_component);
 	g_test_add_func (TPATH "bridge/read-missing-stp", test_read_bridge_missing_stp);
 
-	/* Team */
 	g_test_add_func (TPATH "team/read-master", test_read_team_master);
 	g_test_add_func (TPATH "team/write-master", test_write_team_master);
 	g_test_add_func (TPATH "team/read-port", test_read_team_port);
 	g_test_add_func (TPATH "team/write-port", test_write_team_port);
 	g_test_add_func (TPATH "team/read-port-empty-config", test_read_team_port_empty_config);
 
+	g_test_add_func (TPATH "sit/read/ignore", test_sit_read_ignore);
+
 	/* Stuff we expect to fail for now */
 	g_test_add_func (TPATH "pppoe/write-wired", test_write_wired_pppoe);
 	g_test_add_func (TPATH "vpn/write", test_write_vpn);
diff --git a/src/settings/plugins/ifcfg-rh/utils.h b/src/settings/plugins/ifcfg-rh/utils.h
index 329f4547..752d08a6 100644
--- a/src/settings/plugins/ifcfg-rh/utils.h
+++ b/src/settings/plugins/ifcfg-rh/utils.h
@@ -26,7 +26,7 @@
 #include "shvar.h"
 #include "common.h"
 
-#define NM_IFCFG_CONNECTION_LOG_PATH(path)  str_if_set (path,"in-memory")
+#define NM_IFCFG_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NM_IFCFG_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
 #define NM_IFCFG_CONNECTION_LOG_ARG(con)    NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con))
 #define NM_IFCFG_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index e7d64379..c17824d6 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -582,6 +582,13 @@ write_8021x_setting (NMConnection *connection,
 		svSetValue (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str, FALSE);
 	g_string_free (str, TRUE);
 
+	svSetValue (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH",
+	            nm_setting_802_1x_get_domain_suffix_match (s_8021x),
+	            FALSE);
+	svSetValue (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",
+	            nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x),
+	            FALSE);
+
 	success = write_8021x_certs (s_8021x, FALSE, ifcfg, error);
 	if (success) {
 		/* phase2/inner certs */
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index bf8b9a42..d9ff97dc 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -362,11 +362,17 @@ eap_peap_reader (const char *eap_method,
 		}
 
 		pos = strchr (*iter, '=');
-		pos++;
-		lower = g_ascii_strdown (pos, -1);
-		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower,
-			      NULL);
-		g_free (lower);
+		if (pos && *pos) {
+			pos++;
+			lower = g_ascii_strdown (pos, -1);
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, lower,
+				      NULL);
+			g_free (lower);
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				     "No IEEE_8021X_INNER_AUTH_METHOD.");
+			goto done;
+		}
 		break;
 	}
 
@@ -513,7 +519,7 @@ read_mac_address (const char *conn_name, const char **mac, GError **error)
 	return TRUE;
 }
 
-static void
+static gboolean
 make_wired_connection_setting (NMConnection *connection,
                                const char *conn_name,
                                GError **error)
@@ -538,22 +544,21 @@ make_wired_connection_setting (NMConnection *connection,
 				      (guint32) mtu, NULL);
 	}
 
-	if (read_mac_address (conn_name, &mac, error)) {
-		if (mac) {
-			g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS,
-				      mac, NULL);
-		}
-	} else {
+	if (!read_mac_address (conn_name, &mac, error)) {
 		g_object_unref (s_wired);
-		s_wired = NULL;
+		return FALSE;
 	}
-	if (s_wired)
-		nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	if (mac)
+		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	return TRUE;
 }
 
 /* add NM_SETTING_IP_CONFIG_DHCP_HOSTNAME,
  * NM_SETTING_IP_CONFIG_DHCP_CLIENT_ID in future*/
-static void
+static gboolean
 make_ip4_setting (NMConnection *connection,
                   const char *conn_name,
                   GError **error)
@@ -578,7 +583,7 @@ make_ip4_setting (NMConnection *connection,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 						 "Unknown config for %s", conn_name);
 			g_object_unref (ip4_setting);
-			return;
+			return FALSE;
 		}
 		if (strstr (method, "dhcp"))
 			g_object_set (ip4_setting,
@@ -591,19 +596,19 @@ make_ip4_setting (NMConnection *connection,
 						  NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
 						  NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL);
 			nm_connection_add_setting (connection, NM_SETTING (ip4_setting));
-			return;
+			return TRUE;
 		} else if (strstr (method, "shared")) {
 			g_object_set (ip4_setting,
 						  NM_SETTING_IP_CONFIG_METHOD,
 						  NM_SETTING_IP4_CONFIG_METHOD_SHARED,
 						  NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL);
 			nm_connection_add_setting (connection, NM_SETTING (ip4_setting));
-			return;
+			return TRUE;
 		} else {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 						 "Unknown config for %s", conn_name);
 			g_object_unref (ip4_setting);
-			return;
+			return FALSE;
 		}
 		nm_log_info (LOGD_SETTINGS, "Using %s method for %s", method, conn_name);
 	}else {
@@ -613,7 +618,7 @@ make_ip4_setting (NMConnection *connection,
 				     "Ifnet plugin: can't aquire ip configuration for %s",
 				     conn_name);
 			g_object_unref (ip4_setting);
-			return;
+			return FALSE;
 		}
 		/************** add all ip settings to the connection**********/
 		while (iblock) {
@@ -735,9 +740,11 @@ make_ip4_setting (NMConnection *connection,
 
 	/* Finally add setting to connection */
 	nm_connection_add_setting (connection, NM_SETTING (ip4_setting));
+
+	return TRUE;
 }
 
-static void
+static gboolean
 make_ip6_setting (NMConnection *connection,
                   const char *conn_name,
                   GError **error)
@@ -875,12 +882,12 @@ make_ip6_setting (NMConnection *connection,
 
 done:
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
-	return;
+	return TRUE;
 
 error:
 	g_object_unref (s_ip6);
 	nm_log_warn (LOGD_SETTINGS, "    Ignore IPv6 for %s", conn_name);
-	return;
+	return FALSE;
 }
 
 static NMSetting *
@@ -1018,14 +1025,6 @@ make_leap_setting (const char *ssid, GError **error)
 	wsec =
 	    NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
-	value = wpa_get_value (ssid, "key_mgmt");
-	if (!value || strcmp (value, "IEEE8021X"))
-		goto error;	/* Not LEAP */
-
-	value = wpa_get_value (ssid, "eap");
-	if (!value || strcasecmp (value, "LEAP"))
-		goto error;	/* Not LEAP */
-
 	value = wpa_get_value (ssid, "password");
 	if (value && strlen (value))
 		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
@@ -1444,12 +1443,6 @@ make_wpa_setting (const char *ssid,
 	if (value)
 		adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE;
 
-	value = wpa_get_value (ssid, "key_mgmt");
-	/* Not WPA or Dynamic WEP */
-	if (!value)
-		goto error;
-	if (strcmp (value, "WPA-PSK") && strcmp (value, "WPA-EAP"))
-		goto error;
 	/* Pairwise and Group ciphers */
 	fill_wpa_ciphers (ssid, wsec, FALSE, adhoc);
 	fill_wpa_ciphers (ssid, wsec, TRUE, adhoc);
@@ -1464,6 +1457,7 @@ make_wpa_setting (const char *ssid,
 
 	}
 
+	value = wpa_get_value (ssid, "key_mgmt");
 	if (!strcmp (value, "WPA-PSK")) {
 		char *psk = parse_wpa_psk (wpa_get_value (ssid, "psk"), error);
 
@@ -1522,8 +1516,6 @@ make_wireless_security_setting (const char *conn_name,
 	g_return_val_if_fail (conn_name != NULL
 			      && strcmp (ifnet_get_data (conn_name, "type"),
 					 "ppp") != 0, NULL);
-	if (!wpa_get_value (conn_name, "ssid"))
-		return NULL;
 	nm_log_info (LOGD_SETTINGS, "updating wireless security settings (%s).", conn_name);
 
 	ssid = conn_name;
@@ -1531,19 +1523,22 @@ make_wireless_security_setting (const char *conn_name,
 	if (value)
 		adhoc = strcmp (value, "1") == 0 ? TRUE : FALSE;
 
-	if (!adhoc) {
-		wsec = make_leap_setting (ssid, error);
-		if (error && *error)
-			goto error;
-	}
-	if (!wsec) {
+	value = wpa_get_value (ssid, "key_mgmt");
+	if (!adhoc && g_strcmp0 (value, "IEEE8021X") == 0) {
+		value = wpa_get_value (ssid, "eap");
+		if (value && strcasecmp (value, "LEAP") == 0) {
+			wsec = make_leap_setting (ssid, error);
+			if (wsec == NULL)
+				goto error;
+		}
+	} else if (g_strcmp0 (value, "WPA-PSK") == 0 || g_strcmp0 (value, "WPA-EAP") == 0) {
 		wsec = make_wpa_setting (ssid, basepath, s_8021x, error);
-		if (error && *error)
+		if (wsec == NULL)
 			goto error;
 	}
 	if (!wsec) {
 		wsec = make_wep_setting (ssid, error);
-		if (error && *error)
+		if (wsec == NULL)
 			goto error;
 	}
 
@@ -1559,7 +1554,7 @@ error:
 }
 
 /* Currently only support username and password */
-static void
+static gboolean
 make_pppoe_connection_setting (NMConnection *connection,
                                const char *conn_name,
                                GError **error)
@@ -1575,7 +1570,7 @@ make_pppoe_connection_setting (NMConnection *connection,
 	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			     "ppp requires at lease a username");
-		return;
+		return FALSE;
 	}
 	g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, value, NULL);
 
@@ -1591,6 +1586,8 @@ make_pppoe_connection_setting (NMConnection *connection,
 	/* PPP setting */
 	s_ppp = (NMSettingPpp *) nm_setting_ppp_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_ppp));
+
+	return TRUE;
 }
 
 NMConnection *
@@ -1644,18 +1641,13 @@ ifnet_update_connection_from_config_block (const char *conn_name,
 	if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type)
 	    || !strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) {
 		/* wired setting */
-		make_wired_connection_setting (connection, conn_name, error);
-		if (error && *error) {
-			nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
+		if (!make_wired_connection_setting (connection, conn_name, error))
 			goto error;
-		}
+
 		/* pppoe setting */
-		if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type))
-			make_pppoe_connection_setting (connection, conn_name,
-						       error);
-		if (error && *error) {
-			nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-			goto error;
+		if (!strcmp (NM_SETTING_PPPOE_SETTING_NAME, type)) {
+			if (!make_pppoe_connection_setting (connection, conn_name, error))
+				goto error;
 		}
 	} else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) {
 		/* wireless setting */
@@ -1666,50 +1658,33 @@ ifnet_update_connection_from_config_block (const char *conn_name,
 			goto error;
 		nm_connection_add_setting (connection, wireless_setting);
 
-		if (error && *error) {
-			nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-			goto error;
-		}
-
 		/* wireless security setting */
-		wsec = make_wireless_security_setting (conn_name, basepath, &s_8021x, error);
-		if (wsec) {
+		if (wpa_get_value (conn_name, "ssid")) {
+			wsec = make_wireless_security_setting (conn_name, basepath, &s_8021x, error);
+			if (!wsec)
+				goto error;
 			nm_connection_add_setting (connection, NM_SETTING (wsec));
 			if (s_8021x)
 				nm_connection_add_setting (connection, NM_SETTING (s_8021x));
 		}
-
-		if (error && *error) {
-			nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-			goto error;
-		}
-
 	} else
 		goto error;
 
 	/* IPv4 setting */
-	make_ip4_setting (connection, conn_name, error);
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
+	if (!make_ip4_setting (connection, conn_name, error))
 		goto error;
-	}
 
 	/* IPv6 setting */
-	make_ip6_setting (connection, conn_name, error);
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
+	if (!make_ip6_setting (connection, conn_name, error))
 		goto error;
-	}
 
-	success = nm_connection_verify (connection, error);
-	if (error && *error)
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-	else
+	if (nm_connection_verify (connection, error)) {
 		nm_log_info (LOGD_SETTINGS, "Connection verified %s:%d", conn_name, success);
-	if (!success)
+	} else {
 		goto error;
-	return connection;
+	}
 
+	return connection;
 error:
 	g_object_unref (connection);
 	return NULL;
@@ -2493,8 +2468,8 @@ write_ip4_setting (NMConnection *connection, const char *conn_name, GError **err
 	return success;
 }
 
-static gboolean
-write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name, GError **error)
+static void
+write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name)
 {
 	NMIPRoute *route;
 	const char *next_hop;
@@ -2502,11 +2477,10 @@ write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name, GError **err
 	GString *routes_string;
 	const char *old_routes;
 
-	g_return_val_if_fail (s_ip6 != NULL, FALSE);
+	g_return_if_fail (s_ip6 != NULL);
 	num = nm_setting_ip_config_get_num_routes (s_ip6);
-	if (num == 0) {
-		return TRUE;
-	}
+	if (num == 0)
+		return;
 
 	old_routes = ifnet_get_data (conn_name, "routes");
 	routes_string = g_string_new (old_routes);
@@ -2527,8 +2501,6 @@ write_route6_file (NMSettingIPConfig *s_ip6, const char *conn_name, GError **err
 	if (num > 0)
 		ifnet_set_data (conn_name, "routes", routes_string->str);
 	g_string_free (routes_string, TRUE);
-
-	return TRUE;
 }
 
 static gboolean
@@ -2645,9 +2617,7 @@ write_ip6_setting (NMConnection *connection, const char *conn_name, GError **err
 		g_string_free (searches, TRUE);
 	}
 
-	write_route6_file (s_ip6, conn_name, error);
-	if (error && *error)
-		return FALSE;
+	write_route6_file (s_ip6, conn_name);
 	return TRUE;
 }
 
diff --git a/src/settings/plugins/ifnet/net_parser.c b/src/settings/plugins/ifnet/net_parser.c
index cad34f06..6e1061c6 100644
--- a/src/settings/plugins/ifnet/net_parser.c
+++ b/src/settings/plugins/ifnet/net_parser.c
@@ -552,7 +552,7 @@ gboolean
 ifnet_flush_to_file (const char *config_file, gchar **out_backup)
 {
 	GIOChannel *channel;
-	GError **error = NULL;
+	GError *error = NULL;
 	gpointer key, value, name, network;
 	GHashTableIter iter, iter_network;
 	GList *list_iter;
@@ -579,32 +579,36 @@ ifnet_flush_to_file (const char *config_file, gchar **out_backup)
 	g_io_channel_write_chars (channel,
 				  "#Generated by NetworkManager\n"
 				  "###### Global Configuration ######\n",
-				  -1, &bytes_written, error);
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
+
 	/* Writing global data */
 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 		out_line =
 		    g_strdup_printf ("%s=\"%s\"\n", (gchar *) key, (gchar *) value);
 		g_io_channel_write_chars (channel, out_line, -1,
-					  &bytes_written, error);
-		if (bytes_written == 0 || (error && *error))
-			break;
+					  &bytes_written, &error);
+		if (bytes_written == 0 || error)
+			goto done;
 		g_free (out_line);
 	}
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-		goto done;
-	}
 
 	/* Writing connection data */
 	g_io_channel_write_chars (channel,
 				  "\n###### Connection Configuration ######\n",
-				  -1, &bytes_written, error);
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
+
 	g_hash_table_iter_init (&iter, conn_table);
 	while (g_hash_table_iter_next (&iter, &name, &network)) {
 		g_hash_table_iter_init (&iter_network, (GHashTable *) network);
 		g_io_channel_write_chars (channel,
 					  "#----------------------------------\n",
-					  -1, &bytes_written, error);
+					  -1, &bytes_written, &error);
+		if (error)
+			goto done;
 
 		while (g_hash_table_iter_next (&iter_network, &key, &value)) {
 			if (!g_str_has_prefix ((gchar *) key, "name")
@@ -627,51 +631,47 @@ ifnet_flush_to_file (const char *config_file, gchar **out_backup)
 					    ("%s_%s=\"%s\"\n",
 					     (gchar *) key,
 					     (gchar *) name, (gchar *) value);
-				g_io_channel_write_chars
-				    (channel, out_line, -1,
-				     &bytes_written, error);
-				if (bytes_written == 0 || (error && *error))
-					break;
+				g_io_channel_write_chars (channel, out_line, -1, &bytes_written, &error);
+				if (bytes_written == 0 || error)
+					goto done;
 				g_free (out_line);
 			}
 		}
 	}
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-		goto done;
-	}
 
 	/* Writing reserved functions */
 	if (functions_list) {
 		g_io_channel_write_chars (channel,
 					  "\n###### Reserved Functions ######\n",
-					  -1, &bytes_written, error);
+					  -1, &bytes_written, &error);
+		if (error)
+			goto done;
+
 		/* Writing functions */
 		for (list_iter = functions_list; list_iter;
 		     list_iter = g_list_next (list_iter)) {
 			out_line =
 			    g_strdup_printf ("%s\n", (gchar *) list_iter->data);
 			g_io_channel_write_chars (channel, out_line, -1,
-						  &bytes_written, error);
-			if (bytes_written == 0 || (error && *error))
-				break;
+						  &bytes_written, &error);
+			if (bytes_written == 0 || error)
+				goto done;
 			g_free (out_line);
 		}
-		if (error && *error) {
-			nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-			goto done;
-		}
 	}
 
-	g_io_channel_flush (channel, error);
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
+	g_io_channel_flush (channel, &error);
+	if (error)
 		goto done;
-	}
 	result = TRUE;
 	net_parser_data_changed = FALSE;
 
 done:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "Error writing the configuration file: %s", error->message);
+		g_error_free (error);
+	}
+
 	if (result && out_backup)
 		*out_backup = backup;
 	else
diff --git a/src/settings/plugins/ifnet/net_utils.c b/src/settings/plugins/ifnet/net_utils.c
index 1944e478..bd5d65d5 100644
--- a/src/settings/plugins/ifnet/net_utils.c
+++ b/src/settings/plugins/ifnet/net_utils.c
@@ -800,18 +800,27 @@ gchar *backup_file (const gchar* target)
 {
 	GFile *source, *backup;
 	gchar* backup_path;
-	GError **error = NULL;
+	GError *error = NULL;
 
 	source = g_file_new_for_path (target);
+
+	if (!g_file_query_exists (source, NULL)) {
+		g_object_unref (source);
+		return NULL;
+	}
+
 	backup_path = g_strdup_printf ("%s.bak", target);
 	backup = g_file_new_for_path (backup_path);
 
-	g_file_copy (source, backup, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, error);
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Backup failed: %s", (*error)->message);
+	if (!g_file_copy (source, backup, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) {
+		nm_log_warn (LOGD_SETTINGS, "Backup failed: %s", error->message);
 		g_free (backup_path);
 		backup_path = NULL;
+		g_error_free (error);
 	}
 
+	g_object_unref (source);
+	g_object_unref (backup);
+
 	return backup_path;
 }
diff --git a/src/settings/plugins/ifnet/nm-ifnet-connection.c b/src/settings/plugins/ifnet/nm-ifnet-connection.c
index 84515519..b661b03e 100644
--- a/src/settings/plugins/ifnet/nm-ifnet-connection.c
+++ b/src/settings/plugins/ifnet/nm-ifnet-connection.c
@@ -68,6 +68,8 @@ nm_ifnet_connection_new (NMConnection *source, const char *conn_name)
 	else {
 		tmp = ifnet_update_connection_from_config_block (conn_name, NULL, &error);
 		if (!tmp) {
+			nm_log_warn (LOGD_SETTINGS, "Could not read connection '%s': %s",
+			             conn_name, error->message);
 			g_error_free (error);
 			return NULL;
 		}
@@ -79,11 +81,14 @@ nm_ifnet_connection_new (NMConnection *source, const char *conn_name)
 	object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, NULL);
 	g_assert (object);
 	NM_IFNET_CONNECTION_GET_PRIVATE (object)->conn_name = g_strdup (conn_name);
-	nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
-	                                         tmp,
-	                                         update_unsaved,
-	                                         NULL,
-	                                         NULL);
+	if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
+	                                              tmp,
+	                                              update_unsaved,
+	                                              NULL,
+	                                              NULL)) {
+		g_object_unref (object);
+		return NULL;
+	}
 	g_object_unref (tmp);
 
 	return NM_IFNET_CONNECTION (object);
diff --git a/src/settings/plugins/ifnet/tests/test-ifnet.c b/src/settings/plugins/ifnet/tests/test-ifnet.c
index c870152b..3035fdb1 100644
--- a/src/settings/plugins/ifnet/tests/test-ifnet.c
+++ b/src/settings/plugins/ifnet/tests/test-ifnet.c
@@ -134,7 +134,7 @@ test_convert_ipv4_config_block (void)
 	check_ip_block (iblock, "192.168.4.121", 24, "202.117.16.1");
 	destroy_ip_block (iblock);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*Can't handle IPv4 address*202.117.16.1211*");
 	iblock = convert_ip4_config_block ("eth2");
 	g_test_assert_expected_messages ();
@@ -142,7 +142,7 @@ test_convert_ipv4_config_block (void)
 	check_ip_block (iblock, "192.168.4.121", 24, NULL);
 	destroy_ip_block (iblock);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*missing netmask or prefix*");
 	iblock = convert_ip4_config_block ("eth3");
 	g_assert (iblock == NULL);
@@ -216,9 +216,9 @@ test_new_connection (void)
 	GError *error = NULL;
 	NMConnection *connection;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*Can't handle IPv4 address*202.117.16.1211*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*Can't handle IPv6 address*202.117.16.1211*");
 	connection = ifnet_update_connection_from_config_block ("eth2", NULL, &error);
 	g_test_assert_expected_messages ();
@@ -245,7 +245,7 @@ test_new_connection (void)
 static void
 kill_backup (char **path)
 {
-	if (path) {
+	if (*path) {
 		unlink (*path);
 		g_free (*path);
 		*path = NULL;
@@ -306,9 +306,9 @@ test_add_connection (void)
 	kill_backup (&backup);
 	g_object_unref (connection);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*Can't handle ipv4 address: brd, missing netmask or prefix*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*Can't handle ipv4 address: 202.117.16.255, missing netmask or prefix*");
 	connection = ifnet_update_connection_from_config_block ("myxjtu2", basepath, NULL);
 	g_test_assert_expected_messages ();
@@ -349,10 +349,8 @@ test_missing_config (void)
 	GError *error = NULL;
 	NMConnection *connection;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*Unknown config for eth8*");
 	connection = ifnet_update_connection_from_config_block ("eth8", NULL, &error);
-	g_test_assert_expected_messages ();
+	g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION);
 	g_assert (connection == NULL && error != NULL);
 }
 
diff --git a/src/settings/plugins/ifnet/wpa_parser.c b/src/settings/plugins/ifnet/wpa_parser.c
index 501bca7a..8e2559b3 100644
--- a/src/settings/plugins/ifnet/wpa_parser.c
+++ b/src/settings/plugins/ifnet/wpa_parser.c
@@ -365,7 +365,7 @@ gboolean
 wpa_flush_to_file (const char *config_file)
 {
 	GIOChannel *channel;
-	GError **error = NULL;
+	GError *error = NULL;
 	gpointer key, value, ssid, security;
 	GHashTableIter iter, iter_security;
 	gchar *out_line;
@@ -389,25 +389,27 @@ wpa_flush_to_file (const char *config_file)
 	g_io_channel_write_chars (channel,
 				  "#Generated by NetworkManager\n"
 				  "###### Global Configuration ######\n",
-				  -1, &bytes_written, error);
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
 
 	/* Writing global information */
 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 		out_line =
 		    g_strdup_printf ("%s=%s\n", (gchar *) key, (gchar *) value);
 		g_io_channel_write_chars (channel, out_line, -1, &bytes_written,
-					  error);
-		if (bytes_written == 0 || (error && *error))
+					  &error);
+		if (bytes_written == 0 || error)
 			break;
 		g_free (out_line);
 	}
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
+	if (error)
 		goto done;
-	}
 	g_io_channel_write_chars (channel,
 				  "\n###### Security Configuration ######\n",
-				  -1, &bytes_written, error);
+				  -1, &bytes_written, &error);
+	if (error)
+		goto done;
 
 	g_hash_table_iter_init (&iter, wsec_table);
 	/* Writing security */
@@ -415,35 +417,34 @@ wpa_flush_to_file (const char *config_file)
 		g_hash_table_iter_init (&iter_security,
 					(GHashTable *) security);
 		g_io_channel_write_chars (channel, "network={\n", -1,
-					  &bytes_written, error);
+					  &bytes_written, &error);
+		if (error)
+			goto done;
 		while (g_hash_table_iter_next (&iter_security, &key, &value)) {
 			out_line =
 			    g_strdup_printf (need_quote ((gchar *) key) ?
 					     "\t%s=\"%s\"\n" : "\t%s=%s\n",
 					     (gchar *) key, (gchar *) value);
 			g_io_channel_write_chars (channel, out_line, -1,
-						  &bytes_written, error);
-			if (bytes_written == 0 || (error && *error))
-				break;
+						  &bytes_written, &error);
+			if (bytes_written == 0 || error)
+				goto done;
 			g_free (out_line);
 		}
-		g_io_channel_write_chars (channel,
-					  "}\n\n", -1, &bytes_written, error);
+		g_io_channel_write_chars (channel, "}\n\n", -1, &bytes_written, &error);
 
 	}
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
+	g_io_channel_flush (channel, &error);
+	if (error)
 		goto done;
-	}
-	g_io_channel_flush (channel, error);
 
-	if (error && *error) {
-		nm_log_warn (LOGD_SETTINGS, "Found error: %s", (*error)->message);
-		goto done;
-	}
 	wpa_parser_data_changed = FALSE;
 	result = TRUE;
 done:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "Error writing WPA configuration: %s", error->message);
+		g_error_free (error);
+	}
 	g_io_channel_shutdown (channel, FALSE, NULL);
 	g_io_channel_unref (channel);
 	return result;
diff --git a/src/settings/plugins/ifupdown/interface_parser.c b/src/settings/plugins/ifupdown/interface_parser.c
index be7d387d..d342501b 100644
--- a/src/settings/plugins/ifupdown/interface_parser.c
+++ b/src/settings/plugins/ifupdown/interface_parser.c
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * Tom Parker <palfrey@tevp.net>
@@ -50,7 +50,6 @@ void add_block(const char *type, const char* name)
 		last = ret;
 	}
 	last_data = NULL;
-	//printf("added block '%s' with type '%s'\n",name,type);
 }
 
 void add_data(const char *key,const char *data)
@@ -58,14 +57,15 @@ void add_data(const char *key,const char *data)
 	if_data *ret;
 	char *idx;
 
-	// Check if there is a block where we can attach our data
+	/* Check if there is a block where we can attach our data */
 	if (first == NULL)
 		return;
 
 	ret = (if_data*) calloc(1,sizeof(struct _if_data));
 	ret->key = g_strdup(key);
-	// Normalize keys. Convert '_' to '-', as ifupdown accepts both variants.
-	// When querying keys via ifparser_getkey(), use '-'.
+
+	/* Normalize keys. Convert '_' to '-', as ifupdown accepts both variants.
+	 * When querying keys via ifparser_getkey(), use '-'. */
 	while ((idx = strrchr(ret->key, '_'))) {
 		*idx = '-';
 	}
@@ -81,10 +81,9 @@ void add_data(const char *key,const char *data)
 		last_data->next = ret;
 		last_data = last_data->next;
 	}
-	//printf("added data '%s' with key '%s'\n",data,key);
 }
 
-// join values in src with spaces into dst;  dst needs to be large enough
+/* join values in src with spaces into dst;  dst needs to be large enough */
 static char *join_values_with_spaces(char *dst, char **src)
 {
 	if (dst != NULL) {
@@ -112,7 +111,7 @@ _recursive_ifparser (const char *eni_file, int quiet)
 	int skip_long_line = 0;
 	int offs = 0;
 
-	// Check if interfaces file exists and open it
+	/* Check if interfaces file exists and open it */
 	if (!g_file_test (eni_file, G_FILE_TEST_EXISTS)) {
 		if (!quiet)
 			nm_log_warn (LOGD_SETTINGS, "interfaces file %s doesn't exist\n", eni_file);
@@ -130,8 +129,8 @@ _recursive_ifparser (const char *eni_file, int quiet)
 
 	while (!feof(inp))
 	{
-		char *token[128];	// 255 chars can only be split into 127 tokens
-		char value[255];	// large enough to join previously split tokens
+		char *token[128];	/* 255 chars can only be split into 127 tokens */
+		char value[255];	/* large enough to join previously split tokens */
 		char *safeptr;
 		int toknum;
 		int len = 0;
@@ -141,7 +140,7 @@ _recursive_ifparser (const char *eni_file, int quiet)
 			break;
 
 		len = strlen(line);
-		// skip over-long lines
+		/* skip over-long lines */
 		if (!feof(inp) && len > 0 &&  line[len-1] != '\n') {
 			if (!skip_long_line) {
 				if (!quiet)
@@ -151,35 +150,33 @@ _recursive_ifparser (const char *eni_file, int quiet)
 			continue;
 		}
 
-		// trailing '\n' found: remove it & reset offset to 0
+		/* trailing '\n' found: remove it & reset offset to 0 */
 		if (len > 0 && line[len-1] == '\n') {
 			line[--len] = '\0';
 			offs = 0;
 		}
 
-		// if we're in long_line_skip mode, terminate it for real next line
+		/* if we're in long_line_skip mode, terminate it for real next line */
 		if (skip_long_line) {
 			if (len == 0 || line[len-1] != '\\')
 				skip_long_line = 0;
 			continue;
 		}
 
-		// unwrap wrapped lines
+		/* unwrap wrapped lines */
 		if (len > 0 && line[len-1] == '\\') {
 			offs = len - 1;
 			continue;
 		}
 
-		//printf(">>%s<<\n", line);
-
 #define SPACES	" \t"
-		// tokenize input;
+		/* tokenize input; */
 		for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr);
 		     token[toknum] != NULL;
 		     toknum++, token[toknum] = strtok_r(NULL, SPACES, &safeptr))
 			;
 
-		// ignore comments and empty lines
+		/* ignore comments and empty lines */
 		if (toknum == 0 || *token[0]=='#')
 			continue;
 
@@ -192,11 +189,11 @@ _recursive_ifparser (const char *eni_file, int quiet)
 			continue;
 		}
 
-		// There are five different stanzas:
-		// iface, mapping, auto, allow-* and source.
-		// Create a block for each of them except source.
+		/* There are five different stanzas:
+		 * iface, mapping, auto, allow-* and source.
+		 * Create a block for each of them except source.  */
 
-		// iface stanza takes at least 3 parameters
+		/* iface stanza takes at least 3 parameters */
 		if (strcmp(token[0], "iface") == 0) {
 			if (toknum < 4) {
 				if (!quiet) {
@@ -209,8 +206,8 @@ _recursive_ifparser (const char *eni_file, int quiet)
 			skip_to_block = 0;
 			add_data(token[2], join_values_with_spaces(value, token + 3));
 		}
-		// auto and allow-auto stanzas are equivalent,
-		// both can take multiple interfaces as parameters: add one block for each
+		/* auto and allow-auto stanzas are equivalent,
+		 * both can take multiple interfaces as parameters: add one block for each */
 		else if (strcmp(token[0], "auto") == 0 ||
 			 strcmp(token[0], "allow-auto") == 0) {
 			int i;
@@ -222,14 +219,14 @@ _recursive_ifparser (const char *eni_file, int quiet)
 			add_block(token[0], join_values_with_spaces(value, token + 1));
 			skip_to_block = 0;
 		}
-		// allow-* can take multiple interfaces as parameters: add one block for each
+		/* allow-* can take multiple interfaces as parameters: add one block for each */
 		else if (strncmp(token[0],"allow-",6) == 0) {
 			int i;
 			for (i = 1; i < toknum; i++)
 				add_block(token[0], token[i]);
 			skip_to_block = 0;
 		}
-		// source stanza takes one or more filepaths as parameters
+		/* source stanza takes one or more filepaths as parameters */
 		else if (strcmp(token[0], "source") == 0) {
 			int i;
 			char *en_dir;
diff --git a/src/settings/plugins/ifupdown/interface_parser.h b/src/settings/plugins/ifupdown/interface_parser.h
index 4034d44c..d4bb8515 100644
--- a/src/settings/plugins/ifupdown/interface_parser.h
+++ b/src/settings/plugins/ifupdown/interface_parser.h
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * Tom Parker <palfrey@tevp.net>
diff --git a/src/settings/plugins/ifupdown/nm-ifupdown-connection.c b/src/settings/plugins/ifupdown/nm-ifupdown-connection.c
index 19de1db6..34ecf8b9 100644
--- a/src/settings/plugins/ifupdown/nm-ifupdown-connection.c
+++ b/src/settings/plugins/ifupdown/nm-ifupdown-connection.c
@@ -1,5 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
-
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager system settings service (ifupdown)
  *
  * Alexander Sack <asac@ubuntu.com>
@@ -36,134 +35,46 @@
 
 G_DEFINE_TYPE (NMIfupdownConnection, nm_ifupdown_connection, NM_TYPE_SETTINGS_CONNECTION)
 
-#define NM_IFUPDOWN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionPrivate))
-
-typedef struct {
-	if_block *ifblock;
-} NMIfupdownConnectionPrivate;
-
-enum {
-	PROP_ZERO,
-	PROP_IFBLOCK,
-	_PROP_END,
-};
-
-
 NMIfupdownConnection*
 nm_ifupdown_connection_new (if_block *block)
 {
-	g_return_val_if_fail (block != NULL, NULL);
-
-	return (NMIfupdownConnection *) g_object_new (NM_TYPE_IFUPDOWN_CONNECTION,
-										 NM_IFUPDOWN_CONNECTION_IFBLOCK, block,
-										 NULL);
-}
-
-static gboolean
-supports_secrets (NMSettingsConnection *connection, const char *setting_name)
-{
-	nm_log_info (LOGD_SETTINGS, "supports_secrets() for setting_name: '%s'", setting_name);
-
-	return (strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0);
-}
-
-static void
-nm_ifupdown_connection_init (NMIfupdownConnection *connection)
-{
-}
-
-static GObject *
-constructor (GType type,
-		   guint n_construct_params,
-		   GObjectConstructParam *construct_params)
-{
 	GObject *object;
-	NMIfupdownConnectionPrivate *priv;
 	GError *error = NULL;
 
-	object = G_OBJECT_CLASS (nm_ifupdown_connection_parent_class)->constructor (type, n_construct_params, construct_params);
-	g_return_val_if_fail (object, NULL);
+	g_return_val_if_fail (block != NULL, NULL);
 
-	priv = NM_IFUPDOWN_CONNECTION_GET_PRIVATE (object);
-	if (!priv) {
-		nm_log_warn (LOGD_SETTINGS, "%s.%d - no private instance.", __FILE__, __LINE__);
-		goto err;
-	}
-	if (!priv->ifblock) {
-		nm_log_warn (LOGD_SETTINGS, "(ifupdown) ifblock not provided to constructor.");
-		goto err;
-	}
+	object = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL);
 
-	if (!ifupdown_update_connection_from_if_block (NM_CONNECTION (object), priv->ifblock, &error)) {
-		nm_log_warn (LOGD_SETTINGS, "%s.%d - invalid connection read from /etc/network/interfaces: (%d) %s",
+	if (!ifupdown_update_connection_from_if_block (NM_CONNECTION (object), block, &error)) {
+		nm_log_warn (LOGD_SETTINGS, "%s.%d - invalid connection read from /etc/network/interfaces: %s",
 		             __FILE__,
 		             __LINE__,
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
-		goto err;
+		             error->message);
+		g_object_unref (object);
+		return NULL;
 	}
 
-	return object;
-
- err:
-	g_object_unref (object);
-	return NULL;
+	return (NMIfupdownConnection *) object;
 }
 
-static void
-set_property (GObject *object, guint prop_id,
-		    const GValue *value, GParamSpec *pspec)
+static gboolean
+supports_secrets (NMSettingsConnection *connection, const char *setting_name)
 {
-	NMIfupdownConnectionPrivate *priv = NM_IFUPDOWN_CONNECTION_GET_PRIVATE (object);
-	g_return_if_fail (priv);
+	nm_log_info (LOGD_SETTINGS, "supports_secrets() for setting_name: '%s'", setting_name);
 
-	switch (prop_id) {
-	case PROP_IFBLOCK:
-		priv->ifblock = g_value_get_pointer (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	return (strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0);
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-		    GValue *value, GParamSpec *pspec)
+nm_ifupdown_connection_init (NMIfupdownConnection *connection)
 {
-	NMIfupdownConnectionPrivate *priv = NM_IFUPDOWN_CONNECTION_GET_PRIVATE (object);
-	g_return_if_fail (priv);
-
-	switch (prop_id) {
-	case PROP_IFBLOCK:
-		g_value_set_pointer (value, priv->ifblock);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
 }
 
 static void
 nm_ifupdown_connection_class_init (NMIfupdownConnectionClass *ifupdown_connection_class)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (ifupdown_connection_class);
 	NMSettingsConnectionClass *connection_class = NM_SETTINGS_CONNECTION_CLASS (ifupdown_connection_class);
 
-	g_type_class_add_private (ifupdown_connection_class, sizeof (NMIfupdownConnectionPrivate));
-
-	/* Virtual methods */
-	object_class->constructor  = constructor;
-	object_class->set_property = set_property;
-	object_class->get_property = get_property;
-
 	connection_class->supports_secrets = supports_secrets;
-
-	/* Properties */
-	g_object_class_install_property
-		(object_class, PROP_IFBLOCK,
-		 g_param_spec_pointer (NM_IFUPDOWN_CONNECTION_IFBLOCK, "", "",
-		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                       G_PARAM_STATIC_STRINGS));
 }
 
diff --git a/src/settings/plugins/ifupdown/nm-ifupdown-connection.h b/src/settings/plugins/ifupdown/nm-ifupdown-connection.h
index 54c219f4..8085b831 100644
--- a/src/settings/plugins/ifupdown/nm-ifupdown-connection.h
+++ b/src/settings/plugins/ifupdown/nm-ifupdown-connection.h
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
 /* NetworkManager system settings service (ifupdown)
  *
@@ -37,8 +37,6 @@ G_BEGIN_DECLS
 #define NM_IS_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFUPDOWN_CONNECTION))
 #define NM_IFUPDOWN_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass))
 
-#define NM_IFUPDOWN_CONNECTION_IFBLOCK "ifblock"
-
 typedef struct {
 	NMSettingsConnection parent;
 } NMIfupdownConnection;
diff --git a/src/settings/plugins/ifupdown/parser.c b/src/settings/plugins/ifupdown/parser.c
index bd200cd5..109018dd 100644
--- a/src/settings/plugins/ifupdown/parser.c
+++ b/src/settings/plugins/ifupdown/parser.c
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
 /* NetworkManager system settings service (ifupdown)
  *
@@ -94,6 +94,8 @@ update_wireless_setting_from_if_block(NMConnection *connection,
 	const gchar* value = ifparser_getkey (block, "inet");
 	struct _Mapping mapping[] = {
 		{"ssid", "ssid"},
+		{"essid", "ssid"},
+		{"mode", "mode"},
 		{ NULL, NULL}
 	};
 
@@ -112,14 +114,22 @@ update_wireless_setting_from_if_block(NMConnection *connection,
 			const gchar* newkey = map_by_mapping(mapping, curr->key+wireless_l);
 			nm_log_info (LOGD_SETTINGS, "wireless setting key: %s='%s'", newkey, curr->data);
 			if(newkey && !strcmp("ssid", newkey)) {
-				GByteArray *ssid;
+				GBytes *ssid;
 				gint len = strlen(curr->data);
 
-				ssid = g_byte_array_sized_new (len);
-				g_byte_array_append (ssid, (const guint8 *) curr->data, len);
+				ssid = g_bytes_new (curr->data, len);
 				g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, ssid, NULL);
-				g_byte_array_free (ssid, TRUE);
+				g_bytes_unref (ssid);
 				nm_log_info (LOGD_SETTINGS, "setting wireless ssid = %d", len);
+			} else if(newkey && !strcmp("mode", newkey)) {
+				if (!g_ascii_strcasecmp (curr->data, "Managed") || !g_ascii_strcasecmp (curr->data, "Auto"))
+					g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL);
+				else if (!g_ascii_strcasecmp (curr->data, "Ad-Hoc"))
+					g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_ADHOC, NULL);
+				else if (!g_ascii_strcasecmp (curr->data, "Master"))
+					g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_AP, NULL);
+				else
+					nm_log_warn (LOGD_SETTINGS, "Invalid mode '%s' (not 'Ad-Hoc', 'Ap', 'Managed', or 'Auto')", curr->data);
 			} else {
 				g_object_set(wireless_setting,
 					   newkey, curr->data,
@@ -130,13 +140,12 @@ update_wireless_setting_from_if_block(NMConnection *connection,
 			const gchar* newkey = map_by_mapping(mapping, curr->key+wpa_l);
 
 			if(newkey && !strcmp("ssid", newkey)) {
-				GByteArray *ssid;
+				GBytes *ssid;
 				gint len = strlen(curr->data);
 
-				ssid = g_byte_array_sized_new (len);
-				g_byte_array_append (ssid, (const guint8 *) curr->data, len);
+				ssid = g_bytes_new (curr->data, len);
 				g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, ssid, NULL);
-				g_byte_array_free (ssid, TRUE);
+				g_bytes_unref (ssid);
 				nm_log_info (LOGD_SETTINGS, "setting wpa ssid = %d", len);
 			} else if(newkey) {
 
@@ -318,11 +327,9 @@ update_wireless_security_setting_from_if_block(NMConnection *connection,
 			IfupdownStrDupeFunc dupe_func = map_by_mapping (dupe_mapping, curr->key+wireless_l);
 			IfupdownStrToTypeFunc type_map_func = map_by_mapping (type_mapping, curr->key+wireless_l);
 			GFreeFunc free_func = map_by_mapping (free_type_mapping, curr->key+wireless_l);
-			if(!newkey || !dupe_func) {
-				nm_log_warn (LOGD_SETTINGS, "no (wireless) mapping found for key: %s",
-				             curr->key);
+			if(!newkey || !dupe_func)
 				goto next;
-			}
+
 			property_value = (*dupe_func) (curr->data, connection);
 			nm_log_info (LOGD_SETTINGS, "setting wireless security key: %s=%s",
 			             newkey, property_value);
@@ -353,15 +360,15 @@ update_wireless_security_setting_from_if_block(NMConnection *connection,
 			IfupdownStrDupeFunc dupe_func = map_by_mapping (dupe_mapping, curr->key+wpa_l);
 			IfupdownStrToTypeFunc type_map_func = map_by_mapping (type_mapping, curr->key+wpa_l);
 			GFreeFunc free_func = map_by_mapping (free_type_mapping, curr->key+wpa_l);
-			if(!newkey || !dupe_func) {
+			if(!newkey || !dupe_func)
 				goto next;
-			}
+
 			property_value = (*dupe_func) (curr->data, connection);
 			nm_log_info (LOGD_SETTINGS, "setting wpa security key: %s=%s",
 			             newkey,
 #ifdef DEBUG_SECRETS
 			             property_value
-#else // DEBUG_SECRETS
+#else /* DEBUG_SECRETS */
 			             !strcmp("key", newkey) ||
 			             !strcmp("leap-password", newkey) ||
 			             !strcmp("pin", newkey) ||
@@ -372,7 +379,7 @@ update_wireless_security_setting_from_if_block(NMConnection *connection,
 			             !strcmp("wep-key3", newkey) ||
 			             NULL ?
 			             "<omitted>" : property_value
-#endif // DEBUG_SECRETS
+#endif /* DEBUG_SECRETS */
 			             );
 
 			if (type_map_func) {
diff --git a/src/settings/plugins/ifupdown/parser.h b/src/settings/plugins/ifupdown/parser.h
index 6aebb8c7..ee88c500 100644
--- a/src/settings/plugins/ifupdown/parser.h
+++ b/src/settings/plugins/ifupdown/parser.h
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
 /* NetworkManager system settings service (ifupdown)
  *
diff --git a/src/settings/plugins/ifupdown/plugin.c b/src/settings/plugins/ifupdown/plugin.c
index dbc96eac..b506b2d6 100644
--- a/src/settings/plugins/ifupdown/plugin.c
+++ b/src/settings/plugins/ifupdown/plugin.c
@@ -93,13 +93,13 @@ static void
 settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *req_class);
 
 static void
-SettingsPluginIfupdown_init (NMSettingsPlugin *config);
+init (NMSettingsPlugin *config);
 
 /* Returns the plugins currently known list of connections.  The returned
  * list is freed by the system settings service.
  */
 static GSList*
-SettingsPluginIfupdown_get_connections (NMSettingsPlugin *config);
+get_connections (NMSettingsPlugin *config);
 
 /*
  * Return a list of device specifications which NetworkManager should not
@@ -107,27 +107,27 @@ SettingsPluginIfupdown_get_connections (NMSettingsPlugin *config);
  * each element must be allocated using g_malloc() or its variants.
  */
 static GSList*
-SettingsPluginIfupdown_get_unmanaged_specs (NMSettingsPlugin *config);
+get_unmanaged_specs (NMSettingsPlugin *config);
 
 
 /*  GObject */
 static void
-GObject__get_property (GObject *object, guint prop_id,
+get_property (GObject *object, guint prop_id,
 				   GValue *value, GParamSpec *pspec);
 
 static void
-GObject__set_property (GObject *object, guint prop_id,
+set_property (GObject *object, guint prop_id,
 				   const GValue *value, GParamSpec *pspec);
 
 static void
-GObject__dispose (GObject *object);
+dispose (GObject *object);
 
 static void
 settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
 {
-	plugin_iface->init = SettingsPluginIfupdown_init;
-	plugin_iface->get_connections = SettingsPluginIfupdown_get_connections;
-	plugin_iface->get_unmanaged_specs = SettingsPluginIfupdown_get_unmanaged_specs;
+	plugin_iface->init = init;
+	plugin_iface->get_connections = get_connections;
+	plugin_iface->get_unmanaged_specs = get_unmanaged_specs;
 }
 
 static void
@@ -137,9 +137,9 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *req_class)
 
 	g_type_class_add_private (req_class, sizeof (SettingsPluginIfupdownPrivate));
 
-	object_class->dispose = GObject__dispose;
-	object_class->get_property = GObject__get_property;
-	object_class->set_property = GObject__set_property;
+	object_class->dispose = dispose;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 
 	g_object_class_override_property (object_class,
 	                                  NM_SETTINGS_PLUGIN_PROP_NAME,
@@ -292,7 +292,7 @@ handle_uevent (GUdevClient *client,
 }
 
 static void
-SettingsPluginIfupdown_init (NMSettingsPlugin *config)
+init (NMSettingsPlugin *config)
 {
 	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (config);
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
@@ -447,7 +447,7 @@ SettingsPluginIfupdown_init (NMSettingsPlugin *config)
  * list is freed by the system settings service.
  */
 static GSList*
-SettingsPluginIfupdown_get_connections (NMSettingsPlugin *config)
+get_connections (NMSettingsPlugin *config)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (config);
 	GSList *connections;
@@ -471,7 +471,7 @@ SettingsPluginIfupdown_get_connections (NMSettingsPlugin *config)
  * each element must be allocated using g_malloc() or its variants.
  */
 static GSList*
-SettingsPluginIfupdown_get_unmanaged_specs (NMSettingsPlugin *config)
+get_unmanaged_specs (NMSettingsPlugin *config)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (config);
 	GSList *specs = NULL;
@@ -504,7 +504,7 @@ settings_plugin_ifupdown_init (SettingsPluginIfupdown *plugin)
 }
 
 static void
-GObject__get_property (GObject *object, guint prop_id,
+get_property (GObject *object, guint prop_id,
                        GValue *value, GParamSpec *pspec)
 {
 	switch (prop_id) {
@@ -524,7 +524,7 @@ GObject__get_property (GObject *object, guint prop_id,
 }
 
 static void
-GObject__set_property (GObject *object, guint prop_id,
+set_property (GObject *object, guint prop_id,
 				   const GValue *value, GParamSpec *pspec)
 {
 	switch (prop_id) {
@@ -535,7 +535,7 @@ GObject__set_property (GObject *object, guint prop_id,
 }
 
 static void
-GObject__dispose (GObject *object)
+dispose (GObject *object)
 {
 	SettingsPluginIfupdown *plugin = SETTINGS_PLUGIN_IFUPDOWN (object);
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (plugin);
diff --git a/src/settings/plugins/ifupdown/plugin.h b/src/settings/plugins/ifupdown/plugin.h
index 090178f2..10ba2287 100644
--- a/src/settings/plugins/ifupdown/plugin.h
+++ b/src/settings/plugins/ifupdown/plugin.h
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
 /* NetworkManager system settings service (ifupdown)
  *
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index 29a1141b..6003c745 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -404,10 +404,9 @@ read_connections (NMSettingsPlugin *config)
 
 	dir = g_dir_open (nm_keyfile_plugin_get_path (), 0, &error);
 	if (!dir) {
-		nm_log_warn (LOGD_SETTINGS, "keyfile: cannot read directory '%s': (%d) %s",
+		nm_log_warn (LOGD_SETTINGS, "keyfile: cannot read directory '%s': %s",
 		             nm_keyfile_plugin_get_path (),
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+		             error->message);
 		g_clear_error (&error);
 		return;
 	}
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index c1b79ef7..7b66e198 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -88,33 +88,33 @@ test_read_valid_wired_connection (void)
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
 	gboolean success;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.addresses:*semicolon at the end*addresses1*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.addresses:*semicolon at the end*addresses2*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*missing prefix length*address4*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*missing prefix length*address5*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.routes*semicolon at the end*routes2*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.routes*semicolon at the end*routes3*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.routes*semicolon at the end*routes5*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.routes*semicolon at the end*routes8*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*missing prefix length*address4*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address4*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv6.address*semicolon at the end*address5*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*missing prefix length*address5*");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing prefix length*address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv6.address*semicolon at the end*address7*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv6.routes*semicolon at the end*routes1*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv6.route*semicolon at the end*route6*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection", NULL);
 	g_test_assert_expected_messages ();
@@ -537,11 +537,11 @@ test_read_wired_mac_case (void)
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 };
 	gboolean success;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.addresses*semicolon at the end*addresses1*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv4.addresses*semicolon at the end*addresses2*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*ipv6.routes*semicolon at the end*routes1*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case", NULL);
 	g_test_assert_expected_messages ();
@@ -1513,10 +1513,10 @@ test_read_wired_8021x_tls_blob_connection (void)
 	gboolean success;
 	GBytes *blob;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*<warn>  keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*<warn>  keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob", &error);
 	g_assert_no_error (error);
 	g_assert (connection);
@@ -1576,7 +1576,7 @@ test_read_wired_8021x_tls_bad_path_connection (void)
 	char *tmp2;
 	gboolean success;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
 	                       "*does not exist*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing", &error);
 	g_test_assert_expected_messages ();
@@ -1631,12 +1631,12 @@ test_read_wired_8021x_tls_old_connection (void)
 	const char *tmp;
 	gboolean success;
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*<warn>  keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*<warn>  keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
-	                       "*<warn>  keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*<warn> * keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old", &error);
 	g_assert_no_error (error);
 	g_assert (connection);
@@ -3000,71 +3000,71 @@ int main (int argc, char **argv)
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
 	/* The tests */
-	g_test_add_func ("/keyfile/test_read_valid_wired_connection ", test_read_valid_wired_connection);
-	g_test_add_func ("/keyfile/test_write_wired_connection ", test_write_wired_connection);
+	g_test_add_func ("/keyfile/test_read_valid_wired_connection", test_read_valid_wired_connection);
+	g_test_add_func ("/keyfile/test_write_wired_connection", test_write_wired_connection);
 
-	g_test_add_func ("/keyfile/test_read_ip6_wired_connection ", test_read_ip6_wired_connection);
-	g_test_add_func ("/keyfile/test_write_ip6_wired_connection ", test_write_ip6_wired_connection);
+	g_test_add_func ("/keyfile/test_read_ip6_wired_connection", test_read_ip6_wired_connection);
+	g_test_add_func ("/keyfile/test_write_ip6_wired_connection", test_write_ip6_wired_connection);
 
-	g_test_add_func ("/keyfile/test_read_wired_mac_case ", test_read_wired_mac_case);
-	g_test_add_func ("/keyfile/test_read_mac_old_format ", test_read_mac_old_format);
-	g_test_add_func ("/keyfile/test_read_mac_ib_old_format ", test_read_mac_ib_old_format);
+	g_test_add_func ("/keyfile/test_read_wired_mac_case", test_read_wired_mac_case);
+	g_test_add_func ("/keyfile/test_read_mac_old_format", test_read_mac_old_format);
+	g_test_add_func ("/keyfile/test_read_mac_ib_old_format", test_read_mac_ib_old_format);
 
-	g_test_add_func ("/keyfile/test_read_valid_wireless_connection ", test_read_valid_wireless_connection);
-	g_test_add_func ("/keyfile/test_write_wireless_connection ", test_write_wireless_connection);
+	g_test_add_func ("/keyfile/test_read_valid_wireless_connection", test_read_valid_wireless_connection);
+	g_test_add_func ("/keyfile/test_write_wireless_connection", test_write_wireless_connection);
 
-	g_test_add_func ("/keyfile/test_read_string_ssid ", test_read_string_ssid);
-	g_test_add_func ("/keyfile/test_write_string_ssid ", test_write_string_ssid);
+	g_test_add_func ("/keyfile/test_read_string_ssid", test_read_string_ssid);
+	g_test_add_func ("/keyfile/test_write_string_ssid", test_write_string_ssid);
 
-	g_test_add_func ("/keyfile/test_read_intlist_ssid ", test_read_intlist_ssid);
-	g_test_add_func ("/keyfile/test_write_intlist_ssid ", test_write_intlist_ssid);
+	g_test_add_func ("/keyfile/test_read_intlist_ssid", test_read_intlist_ssid);
+	g_test_add_func ("/keyfile/test_write_intlist_ssid", test_write_intlist_ssid);
 
-	g_test_add_func ("/keyfile/test_read_intlike_ssid ", test_read_intlike_ssid);
-	g_test_add_func ("/keyfile/test_write_intlike_ssid ", test_write_intlike_ssid);
+	g_test_add_func ("/keyfile/test_read_intlike_ssid", test_read_intlike_ssid);
+	g_test_add_func ("/keyfile/test_write_intlike_ssid", test_write_intlike_ssid);
 
-	g_test_add_func ("/keyfile/test_read_intlike_ssid_2 ", test_read_intlike_ssid_2);
-	g_test_add_func ("/keyfile/test_write_intlike_ssid_2 ", test_write_intlike_ssid_2);
+	g_test_add_func ("/keyfile/test_read_intlike_ssid_2", test_read_intlike_ssid_2);
+	g_test_add_func ("/keyfile/test_write_intlike_ssid_2", test_write_intlike_ssid_2);
 
-	g_test_add_func ("/keyfile/test_read_bt_dun_connection ", test_read_bt_dun_connection);
-	g_test_add_func ("/keyfile/test_write_bt_dun_connection ", test_write_bt_dun_connection);
+	g_test_add_func ("/keyfile/test_read_bt_dun_connection", test_read_bt_dun_connection);
+	g_test_add_func ("/keyfile/test_write_bt_dun_connection", test_write_bt_dun_connection);
 
-	g_test_add_func ("/keyfile/test_read_gsm_connection ", test_read_gsm_connection);
-	g_test_add_func ("/keyfile/test_write_gsm_connection ", test_write_gsm_connection);
+	g_test_add_func ("/keyfile/test_read_gsm_connection", test_read_gsm_connection);
+	g_test_add_func ("/keyfile/test_write_gsm_connection", test_write_gsm_connection);
 
-	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_blob_connection ", test_read_wired_8021x_tls_blob_connection);
-	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_bad_path_connection ", test_read_wired_8021x_tls_bad_path_connection);
+	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_blob_connection", test_read_wired_8021x_tls_blob_connection);
+	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_bad_path_connection", test_read_wired_8021x_tls_bad_path_connection);
 
-	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_old_connection ", test_read_wired_8021x_tls_old_connection);
-	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_new_connection ", test_read_wired_8021x_tls_new_connection);
-	g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_path ", test_write_wired_8021x_tls_connection_path);
-	g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_blob ", test_write_wired_8021x_tls_connection_blob);
+	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_old_connection", test_read_wired_8021x_tls_old_connection);
+	g_test_add_func ("/keyfile/test_read_wired_8021x_tls_new_connection", test_read_wired_8021x_tls_new_connection);
+	g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_path", test_write_wired_8021x_tls_connection_path);
+	g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_blob", test_write_wired_8021x_tls_connection_blob);
 
-	g_test_add_func ("/keyfile/test_read_infiniband_connection ", test_read_infiniband_connection);
-	g_test_add_func ("/keyfile/test_write_infiniband_connection ", test_write_infiniband_connection);
+	g_test_add_func ("/keyfile/test_read_infiniband_connection", test_read_infiniband_connection);
+	g_test_add_func ("/keyfile/test_write_infiniband_connection", test_write_infiniband_connection);
 
-	g_test_add_func ("/keyfile/test_read_bridge_main ", test_read_bridge_main);
-	g_test_add_func ("/keyfile/test_write_bridge_main ", test_write_bridge_main);
-	g_test_add_func ("/keyfile/test_read_bridge_component ", test_read_bridge_component);
-	g_test_add_func ("/keyfile/test_write_bridge_component ", test_write_bridge_component);
+	g_test_add_func ("/keyfile/test_read_bridge_main", test_read_bridge_main);
+	g_test_add_func ("/keyfile/test_write_bridge_main", test_write_bridge_main);
+	g_test_add_func ("/keyfile/test_read_bridge_component", test_read_bridge_component);
+	g_test_add_func ("/keyfile/test_write_bridge_component", test_write_bridge_component);
 
-	g_test_add_func ("/keyfile/test_read_new_wired_group_name ", test_read_new_wired_group_name);
-	g_test_add_func ("/keyfile/test_write_new_wired_group_name ", test_write_new_wired_group_name);
-	g_test_add_func ("/keyfile/test_read_new_wireless_group_names ", test_read_new_wireless_group_names);
-	g_test_add_func ("/keyfile/test_write_new_wireless_group_names ", test_write_new_wireless_group_names);
+	g_test_add_func ("/keyfile/test_read_new_wired_group_name", test_read_new_wired_group_name);
+	g_test_add_func ("/keyfile/test_write_new_wired_group_name", test_write_new_wired_group_name);
+	g_test_add_func ("/keyfile/test_read_new_wireless_group_names", test_read_new_wireless_group_names);
+	g_test_add_func ("/keyfile/test_write_new_wireless_group_names", test_write_new_wireless_group_names);
 
-	g_test_add_func ("/keyfile/test_read_missing_vlan_setting ", test_read_missing_vlan_setting);
-	g_test_add_func ("/keyfile/test_read_missing_vlan_flags ", test_read_missing_vlan_flags);
-	g_test_add_func ("/keyfile/test_read_missing_id_uuid ", test_read_missing_id_uuid);
+	g_test_add_func ("/keyfile/test_read_missing_vlan_setting", test_read_missing_vlan_setting);
+	g_test_add_func ("/keyfile/test_read_missing_vlan_flags", test_read_missing_vlan_flags);
+	g_test_add_func ("/keyfile/test_read_missing_id_uuid", test_read_missing_id_uuid);
 
 	g_test_add_func ("/keyfile/test_read_minimal", test_read_minimal);
 	g_test_add_func ("/keyfile/test_read_minimal_slave", test_read_minimal_slave);
 
-	g_test_add_func ("/keyfile/test_read_enum_property ", test_read_enum_property);
-	g_test_add_func ("/keyfile/test_write_enum_property ", test_write_enum_property);
-	g_test_add_func ("/keyfile/test_read_flags_property ", test_read_flags_property);
-	g_test_add_func ("/keyfile/test_write_flags_property ", test_write_flags_property);
+	g_test_add_func ("/keyfile/test_read_enum_property", test_read_enum_property);
+	g_test_add_func ("/keyfile/test_write_enum_property", test_write_enum_property);
+	g_test_add_func ("/keyfile/test_read_flags_property", test_read_flags_property);
+	g_test_add_func ("/keyfile/test_write_flags_property", test_write_flags_property);
 
-	g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename ", test_nm_keyfile_plugin_utils_escape_filename);
+	g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename);
 
 	return g_test_run ();
 }
diff --git a/src/settings/plugins/keyfile/utils.h b/src/settings/plugins/keyfile/utils.h
index 0b2b6f4e..c18fb2bc 100644
--- a/src/settings/plugins/keyfile/utils.h
+++ b/src/settings/plugins/keyfile/utils.h
@@ -27,7 +27,7 @@
 #define KEYFILE_PLUGIN_NAME "keyfile"
 #define KEYFILE_PLUGIN_INFO "(c) 2007 - 2015 Red Hat, Inc.  To report bugs please use the NetworkManager mailing list."
 
-#define NM_KEYFILE_CONNECTION_LOG_PATH(path)  str_if_set (path,"in-memory")
+#define NM_KEYFILE_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NM_KEYFILE_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
 #define NM_KEYFILE_CONNECTION_LOG_ARG(con)    NM_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con))
 #define NM_KEYFILE_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
index 091742e5..5ce8bb31 100644
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -761,7 +761,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 	} else {
 		/* 802.1x for Dynamic WEP and WPA-Enterprise */
 		if (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) {
-		    if (!setting_8021x) {
+			if (!setting_8021x) {
 				g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
 				             "Cannot set key-mgmt %s with missing 8021x setting", key_mgmt);
 				return FALSE;
@@ -1033,6 +1033,14 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 	if (!ADD_STRING_LIST_VAL (self, setting, 802_1x, phase2_altsubject_match, phase2_altsubject_matches, "altsubject_match2", ';', FALSE, FALSE, error))
 		return FALSE;
 
+	/* Domain suffix match */
+	value = nm_setting_802_1x_get_domain_suffix_match (setting);
+	if (!add_string_val (self, value, "domain_suffix_match", FALSE, FALSE, error))
+		return FALSE;
+	value = nm_setting_802_1x_get_phase2_domain_suffix_match (setting);
+	if (!add_string_val (self, value, "domain_suffix_match2", FALSE, FALSE, error))
+		return FALSE;
+
 	/* Private key */
 	added = FALSE;
 	switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c
index 1b352527..f2c63364 100644
--- a/src/supplicant-manager/nm-supplicant-manager.c
+++ b/src/supplicant-manager/nm-supplicant-manager.c
@@ -34,6 +34,16 @@
 
 G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT)
 
+#define _NMLOG_DOMAIN         LOGD_SUPPLICANT
+#define _NMLOG_PREFIX_NAME    "supplicant"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        nm_log ((level), _NMLOG_DOMAIN, \
+                "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                _NMLOG_PREFIX_NAME": " \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+    } G_STMT_END
+
 typedef struct {
 	GDBusProxy *     proxy;
 	GCancellable *   cancellable;
@@ -133,7 +143,7 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self,
 
 	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
+	_LOGD ("(%s): creating new supplicant interface", ifname);
 
 	/* assert against not requesting duplicate interfaces. */
 	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) {
@@ -195,9 +205,9 @@ update_capabilities (NMSupplicantManager *self)
 	for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next)
 		nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support);
 
-	nm_log_dbg (LOGD_SUPPLICANT, "AP mode is %ssupported",
-	            (priv->ap_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
-	                (priv->ap_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
+	_LOGD ("AP mode is %ssupported",
+	       (priv->ap_support == NM_SUPPLICANT_FEATURE_YES) ? "" :
+	           (priv->ap_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly ");
 
 	/* EAP-FAST */
 	priv->fast_supported = FALSE;
@@ -212,7 +222,7 @@ update_capabilities (NMSupplicantManager *self)
 		g_variant_unref (value);
 	}
 
-	nm_log_dbg (LOGD_SUPPLICANT, "EAP-FAST is %ssupported", priv->fast_supported ? "" : "not ");
+	_LOGD ("EAP-FAST is %ssupported", priv->fast_supported ? "" : "not ");
 }
 
 static void
@@ -270,7 +280,7 @@ wpas_die_count_reset_cb (gpointer user_data)
 	/* Reset the die count back to zero, which allows use of the supplicant again */
 	priv->die_count_reset_id = 0;
 	set_die_count (self, 0);
-	nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant die count reset");
+	_LOGI ("wpa_supplicant die count reset");
 	return FALSE;
 }
 
@@ -284,7 +294,7 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
 	g_return_if_fail (proxy == priv->proxy);
 
 	owner = g_dbus_proxy_get_name_owner (proxy);
-	nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant %s", owner ? "running" : "stopped");
+	_LOGI ("wpa_supplicant %s", owner ? "running" : "stopped");
 
 	if (owner) {
 		set_running (self, TRUE);
@@ -301,9 +311,8 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data)
 		set_die_count (self, priv->die_count + 1);
 
 		if (die_count_exceeded (priv->die_count)) {
-			nm_log_info (LOGD_SUPPLICANT,
-			             "wpa_supplicant die count %d; ignoring for 10 seconds",
-			             priv->die_count);
+			_LOGI ("wpa_supplicant die count %d; ignoring for 10 seconds",
+			       priv->die_count);
 		}
 
 		set_running (self, FALSE);
@@ -324,9 +333,8 @@ on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data)
 
 	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
 	if (!proxy) {
-		nm_log_warn (LOGD_SUPPLICANT,
-		             "Failed to acquire wpa_supplicant proxy: Wi-Fi and 802.1x will not be available (%s)",
-		             error->message);
+		_LOGW ("failed to acquire wpa_supplicant proxy: Wi-Fi and 802.1x will not be available (%s)",
+		       error->message);
 		g_clear_error (&error);
 		return;
 	}
diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.c b/src/supplicant-manager/nm-supplicant-settings-verify.c
index ec660d18..bb046f93 100644
--- a/src/supplicant-manager/nm-supplicant-settings-verify.c
+++ b/src/supplicant-manager/nm-supplicant-settings-verify.c
@@ -112,6 +112,7 @@ static const struct Opt opt_table[] = {
 	{ "ca_path",            TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "subject_match",      TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "altsubject_match",   TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "domain_suffix_match",TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "ca_cert",            TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key",        TYPE_BYTES,   0, 65536, FALSE,  NULL },
@@ -122,6 +123,7 @@ static const struct Opt opt_table[] = {
 	{ "ca_path2",           TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "subject_match2",     TYPE_BYTES,   0, 0, FALSE,  NULL },
 	{ "altsubject_match2",  TYPE_BYTES,   0, 0, FALSE,  NULL },
+	{ "domain_suffix_match2", TYPE_BYTES, 0, 0, FALSE,  NULL },
 	{ "ca_cert2",           TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "client_cert2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
 	{ "private_key2",       TYPE_BYTES,   0, 65536, FALSE,  NULL },
diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c
index 0c3e9a47..881f44f6 100644
--- a/src/supplicant-manager/tests/test-supplicant-config.c
+++ b/src/supplicant-manager/tests/test-supplicant-config.c
@@ -149,13 +149,13 @@ test_wifi_open (void)
 
 	config = nm_supplicant_config_new ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'ssid' value 'Test SSID'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'scan_ssid' value '1'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'bssid' value '11:22:33:44:55:66'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'freq_list' value *");
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
@@ -166,7 +166,7 @@ test_wifi_open (void)
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'key_mgmt' value 'NONE'");
 	g_assert (nm_supplicant_config_add_no_security (config, &error));
 	g_assert_no_error (error);
@@ -254,13 +254,13 @@ test_wifi_wep_key (const char *detail,
 
 	config = nm_supplicant_config_new ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'ssid' value 'Test SSID'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'scan_ssid' value '1'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'bssid' value '11:22:33:44:55:66'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'freq_list' value *");
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
@@ -271,11 +271,11 @@ test_wifi_wep_key (const char *detail,
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'key_mgmt' value 'NONE'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'wep_key0' value *");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'wep_tx_keyidx' value '0'");
 	g_assert (nm_supplicant_config_add_setting_wireless_security (config,
 	                                                              s_wsec,
@@ -399,13 +399,13 @@ test_wifi_wpa_psk (const char *detail,
 
 	config = nm_supplicant_config_new ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'ssid' value 'Test SSID'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'scan_ssid' value '1'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'bssid' value '11:22:33:44:55:66'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'freq_list' value *");
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
@@ -416,15 +416,15 @@ test_wifi_wpa_psk (const char *detail,
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'key_mgmt' value 'WPA-PSK'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'psk' value *");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'proto' value 'WPA RSN'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'pairwise' value 'TKIP CCMP'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'group' value 'TKIP CCMP'");
 	g_assert (nm_supplicant_config_add_setting_wireless_security (config,
 	                                                              s_wsec,
@@ -546,13 +546,13 @@ test_wifi_eap (void)
 
 	config = nm_supplicant_config_new ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'ssid' value 'Test SSID'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'scan_ssid' value '1'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'bssid' value '11:22:33:44:55:66'*");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'freq_list' value *");
 	g_assert (nm_supplicant_config_add_setting_wireless (config,
 	                                                     s_wifi,
@@ -563,25 +563,25 @@ test_wifi_eap (void)
 	g_assert_no_error (error);
 	g_test_assert_expected_messages ();
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'key_mgmt' value 'WPA-EAP'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'proto' value 'WPA RSN'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'pairwise' value 'TKIP CCMP'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*added 'group' value 'TKIP CCMP'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*Config: added 'eap' value 'TLS'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*Config: added 'fragment_size' value '1086'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "* Config: added 'ca_cert' value '*/test-ca-cert.pem'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "* Config: added 'private_key' value '*/test-cert.p12'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*Config: added 'bgscan' value 'simple:30:-65:300'");
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO,
 	                       "*Config: added 'proactive_key_caching' value '1'");
 	g_assert (nm_supplicant_config_add_setting_wireless_security (config,
 	                                                              s_wsec,
diff --git a/src/systemd/nm-sd-adapt.c b/src/systemd/nm-sd-adapt.c
index fa7cd938..4e308276 100644
--- a/src/systemd/nm-sd-adapt.c
+++ b/src/systemd/nm-sd-adapt.c
@@ -13,226 +13,22 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
 
 #include "nm-sd-adapt.h"
 
-#include <unistd.h>
-#include <errno.h>
-
-#include "sd-event.h"
 #include "fd-util.h"
-#include "time-util.h"
-
-struct sd_event_source {
-	guint refcount;
-	guint id;
-	gpointer user_data;
-
-	GIOChannel *channel;
-
-	union {
-		struct {
-			sd_event_io_handler_t cb;
-		} io;
-		struct {
-			sd_event_time_handler_t cb;
-			uint64_t usec;
-		} time;
-	};
-};
-
-static struct sd_event_source *
-source_new (void)
-{
-	struct sd_event_source *source;
-
-	source = g_slice_new0 (struct sd_event_source);
-	source->refcount = 1;
-	return source;
-}
-
-int
-sd_event_source_set_priority (sd_event_source *s, int64_t priority)
-{
-	return 0;
-}
-
-sd_event_source*
-sd_event_source_unref (sd_event_source *s)
-{
-
-	if (!s)
-		return NULL;
-
-	g_return_val_if_fail (s->refcount, NULL);
-
-	s->refcount--;
-	if (s->refcount == 0) {
-		if (s->id)
-			g_source_remove (s->id);
-		if (s->channel) {
-			/* Don't shut down the channel since systemd will soon close
-			 * the file descriptor itself, which would cause -EBADF.
-			 */
-			g_io_channel_unref (s->channel);
-		}
-		g_slice_free (struct sd_event_source, s);
-	}
-	return NULL;
-}
-
-int
-sd_event_source_set_description(sd_event_source *s, const char *description)
-{
-	if (!s)
-		return -EINVAL;
-
-	g_source_set_name_by_id (s->id, description);
-	return 0;
-}
-
-static gboolean
-io_ready (GIOChannel *channel, GIOCondition condition, struct sd_event_source *source)
-{
-	int r, revents = 0;
-	gboolean result;
-
-	if (condition & G_IO_IN)
-		revents |= EPOLLIN;
-	if (condition & G_IO_OUT)
-		revents |= EPOLLOUT;
-	if (condition & G_IO_PRI)
-		revents |= EPOLLPRI;
-	if (condition & G_IO_ERR)
-		revents |= EPOLLERR;
-	if (condition & G_IO_HUP)
-		revents |= EPOLLHUP;
-
-	source->refcount++;
-
-	r = source->io.cb (source, g_io_channel_unix_get_fd (channel), revents, source->user_data);
-	if (r < 0 || source->refcount <= 1) {
-		source->id = 0;
-		result = G_SOURCE_REMOVE;
-	} else
-		result = G_SOURCE_CONTINUE;
-
-	sd_event_source_unref (source);
-
-	return result;
-}
-
-int
-sd_event_add_io (sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata)
-{
-	struct sd_event_source *source;
-	GIOChannel *channel;
-	GIOCondition condition = 0;
-
-	/* systemd supports floating sd_event_source by omitting the @s argument.
-	 * We don't have such users and don't implement floating references. */
-	g_return_val_if_fail (s, -EINVAL);
 
-	channel = g_io_channel_unix_new (fd);
-	if (!channel)
-		return -EINVAL;
-
-	source = source_new ();
-	source->io.cb = callback;
-	source->user_data = userdata;
-	source->channel = channel;
-
-	if (events & EPOLLIN)
-		condition |= G_IO_IN;
-	if (events & EPOLLOUT)
-		condition |= G_IO_OUT;
-	if (events & EPOLLPRI)
-		condition |= G_IO_PRI;
-	if (events & EPOLLERR)
-		condition |= G_IO_ERR;
-	if (events & EPOLLHUP)
-		condition |= G_IO_HUP;
-
-	g_io_channel_set_encoding (source->channel, NULL, NULL);
-	g_io_channel_set_buffered (source->channel, FALSE);
-	source->id = g_io_add_watch (source->channel, condition, (GIOFunc) io_ready, source);
-
-	*s = source;
-	return 0;
-}
-
-static gboolean
-time_ready (struct sd_event_source *source)
-{
-	source->refcount++;
-
-	source->time.cb (source, source->time.usec, source->user_data);
-	source->id = 0;
-
-	sd_event_source_unref (source);
-
-	return G_SOURCE_REMOVE;
-}
+/*****************************************************************************/
 
 int
-sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata)
-{
-	struct sd_event_source *source;
-	uint64_t n = now (clock);
-
-	/* systemd supports floating sd_event_source by omitting the @s argument.
-	 * We don't have such users and don't implement floating references. */
-	g_return_val_if_fail (s, -EINVAL);
-
-	source = source_new ();
-	source->time.cb = callback;
-	source->user_data = userdata;
-	source->time.usec = usec;
-
-	if (usec > 1000)
-		usec = n < usec - 1000 ? usec - n : 1000;
-	source->id = g_timeout_add (usec / 1000, (GSourceFunc) time_ready, source);
-
-	*s = source;
-	return 0;
-}
-
-/* sd_event is basically a GMainContext; but since we only
- * ever use the default context, nothing to do here.
- */
-
-int
-sd_event_default (sd_event **e)
-{
-	*e = GUINT_TO_POINTER (1);
-	return 0;
-}
-
-sd_event*
-sd_event_ref (sd_event *e)
-{
-	return e;
-}
-
-sd_event*
-sd_event_unref (sd_event *e)
-{
-	return NULL;
-}
-
-int
-sd_event_now (sd_event *e, clockid_t clock, uint64_t *usec)
-{
-	*usec = now (clock);
-	return 0;
-}
-
-int asynchronous_close(int fd) {
-	safe_close(fd);
+asynchronous_close (int fd) {
+	safe_close (fd);
 	return -1;
 }
 
+/*****************************************************************************/
+
diff --git a/src/systemd/nm-sd-adapt.h b/src/systemd/nm-sd-adapt.h
index f48c86f1..010c8a80 100644
--- a/src/systemd/nm-sd-adapt.h
+++ b/src/systemd/nm-sd-adapt.h
@@ -53,7 +53,7 @@ _slog_level_to_nm (int slevel)
 	const int _nm_e = (error); \
 	const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
 	\
-	if (nm_logging_enabled (_nm_l, LOGD_DHCP)) { \
+	if (nm_logging_enabled (_nm_l, LOGD_SYSTEMD)) { \
 		const char *_nm_location = strrchr ((""file), '/'); \
 		\
 		_nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
@@ -102,6 +102,7 @@ G_STMT_START { \
 #endif
 #include <unistd.h>
 #include <sys/syscall.h>
+#include <sys/ioctl.h>
 
 #include <net/if_arp.h>
 
@@ -110,6 +111,18 @@ G_STMT_START { \
 #define BPF_XOR 0xa0
 #endif
 
+#ifndef ETHERTYPE_LLDP
+#define ETHERTYPE_LLDP 0x88cc
+#endif
+
+#ifndef HAVE_SECURE_GETENV
+#  ifdef HAVE___SECURE_GETENV
+#    define secure_getenv __secure_getenv
+#  else
+#    error neither secure_getenv nor __secure_getenv is available
+#  endif
+#endif
+
 /*****************************************************************************/
 
 /* work around missing uchar.h */
@@ -118,6 +131,14 @@ typedef guint32 char32_t;
 
 /*****************************************************************************/
 
+#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+
+static inline int
+sd_notify (int unset_environment, const char *state)
+{
+	return 0;
+}
+
 /* Can't include both net/if.h and linux/if.h; so have to define this here */
 #ifndef IFNAMSIZ
 #define IFNAMSIZ 16
diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c
new file mode 100644
index 00000000..4d596020
--- /dev/null
+++ b/src/systemd/nm-sd.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 - 2016 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-sd.h"
+
+#include "sd-event.h"
+
+/*****************************************************************************
+ * Integrating sd_event into glib. Taken and adjusted from
+ * https://www.freedesktop.org/software/systemd/man/sd_event_get_fd.html
+ *****************************************************************************/
+
+typedef struct SDEventSource {
+	GSource source;
+	GPollFD pollfd;
+	sd_event *event;
+	guint *default_source_id;
+} SDEventSource;
+
+static gboolean
+event_prepare (GSource *source, gint *timeout_)
+{
+	return sd_event_prepare (((SDEventSource *) source)->event) > 0;
+}
+
+static gboolean
+event_check (GSource *source)
+{
+	return sd_event_wait (((SDEventSource *) source)->event, 0) > 0;
+}
+
+static gboolean
+event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+	return sd_event_dispatch (((SDEventSource *)source)->event) > 0;
+}
+
+static void
+event_finalize (GSource *source)
+{
+	SDEventSource *s;
+
+	s = (SDEventSource *) source;
+	sd_event_unref (s->event);
+	if (s->default_source_id)
+		*s->default_source_id = 0;
+}
+
+static SDEventSource *
+event_create_source (sd_event *event, guint *default_source_id)
+{
+	static GSourceFuncs event_funcs = {
+		.prepare = event_prepare,
+		.check = event_check,
+		.dispatch = event_dispatch,
+		.finalize = event_finalize,
+	};
+	SDEventSource *source;
+
+	g_return_val_if_fail (event, NULL);
+
+	source = (SDEventSource *) g_source_new (&event_funcs, sizeof (SDEventSource));
+
+	source->event = sd_event_ref (event);
+	source->pollfd.fd = sd_event_get_fd (event);
+	source->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+	source->default_source_id = default_source_id;
+
+	g_source_add_poll ((GSource *) source, &source->pollfd);
+
+	return source;
+}
+
+static guint
+event_attach (sd_event *event, GMainContext *context)
+{
+	SDEventSource *source;
+	guint id;
+	int r;
+	sd_event *e = event;
+	guint *p_default_source_id = NULL;
+
+	if (!e) {
+		static guint default_source_id = 0;
+
+		if (default_source_id) {
+			/* The default event cannot be registered multiple times. */
+			g_return_val_if_reached (0);
+		}
+
+		r = sd_event_default (&e);
+		if (r < 0)
+			g_return_val_if_reached (0);
+
+		p_default_source_id = &default_source_id;
+	}
+
+	source = event_create_source (e, p_default_source_id);
+	id = g_source_attach ((GSource *) source, context);
+	g_source_unref ((GSource *) source);
+
+
+	if (!event) {
+		*p_default_source_id = id;
+		sd_event_unref (e);
+	}
+
+	g_return_val_if_fail (id, 0);
+	return id;
+}
+
+guint
+nm_sd_event_attach_default (void)
+{
+	return event_attach (NULL, NULL);
+}
+
+/*****************************************************************************/
+
diff --git a/src/systemd/nm-sd.h b/src/systemd/nm-sd.h
new file mode 100644
index 00000000..888d44ab
--- /dev/null
+++ b/src/systemd/nm-sd.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 - 2016 Red Hat, Inc.
+ */
+
+#ifndef __NM_SD_H__
+#define __NM_SD_H__
+
+guint nm_sd_event_attach_default (void);
+
+#endif /* __NM_SD_H__ */
+
diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h
index 679ba7f3..ceeee519 100644
--- a/src/systemd/src/basic/alloc-util.h
+++ b/src/systemd/src/basic/alloc-util.h
@@ -51,25 +51,29 @@ static inline void freep(void *p) {
 
 #define _cleanup_free_ _cleanup_(freep)
 
-_malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
-        if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+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) {
+        if (size_multiply_overflow(size, need))
                 return NULL;
 
-        return malloc(a * b);
+        return malloc(size * need);
 }
 
-_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) {
-        if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) {
+        if (size_multiply_overflow(size, need))
                 return NULL;
 
-        return realloc(p, a * b);
+        return realloc(p, size * need);
 }
 
-_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) {
-        if (_unlikely_(b != 0 && a > ((size_t) -1) / b))
+_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
+        if (size_multiply_overflow(size, need))
                 return NULL;
 
-        return memdup(p, a * b);
+        return memdup(p, size * need);
 }
 
 void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c
index 34fb0ced..406b791f 100644
--- a/src/systemd/src/basic/escape.c
+++ b/src/systemd/src/basic/escape.c
@@ -415,6 +415,34 @@ char *xescape(const char *s, const char *bad) {
         return r;
 }
 
+char *octescape(const char *s, size_t len) {
+        char *r, *t;
+        const char *f;
+
+        /* Escapes all chars in bad, in addition to \ and " chars,
+         * in \nnn style escaping. */
+
+        r = new(char, len * 4 + 1);
+        if (!r)
+                return NULL;
+
+        for (f = s, t = r; f < s + len; f++) {
+
+                if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') {
+                        *(t++) = '\\';
+                        *(t++) = '0' + (*f >> 6);
+                        *(t++) = '0' + ((*f >> 3) & 8);
+                        *(t++) = '0' + (*f & 8);
+                } else
+                        *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return r;
+
+}
+
 static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
         assert(bad);
 
diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h
index ce518111..b8ec96ad 100644
--- a/src/systemd/src/basic/escape.h
+++ b/src/systemd/src/basic/escape.h
@@ -52,6 +52,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
 
 char *xescape(const char *s, const char *bad);
+char *octescape(const char *s, size_t len);
 
 char *shell_escape(const char *s, const char *bad);
 char *shell_maybe_quote(const char *s);
diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c
new file mode 100644
index 00000000..0219300d
--- /dev/null
+++ b/src/systemd/src/basic/ether-addr-util.c
@@ -0,0 +1,58 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ether-addr-util.h"
+#include "macro.h"
+
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
+        assert(addr);
+        assert(buffer);
+
+        /* Like ether_ntoa() but uses %02x instead of %x to print
+         * ethernet addresses, which makes them look less funny. Also,
+         * doesn't use a static buffer. */
+
+        sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
+                addr->ether_addr_octet[0],
+                addr->ether_addr_octet[1],
+                addr->ether_addr_octet[2],
+                addr->ether_addr_octet[3],
+                addr->ether_addr_octet[4],
+                addr->ether_addr_octet[5]);
+
+        return buffer;
+}
+
+bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
+        assert(a);
+        assert(b);
+
+        return  a->ether_addr_octet[0] == b->ether_addr_octet[0] &&
+                a->ether_addr_octet[1] == b->ether_addr_octet[1] &&
+                a->ether_addr_octet[2] == b->ether_addr_octet[2] &&
+                a->ether_addr_octet[3] == b->ether_addr_octet[3] &&
+                a->ether_addr_octet[4] == b->ether_addr_octet[4] &&
+                a->ether_addr_octet[5] == b->ether_addr_octet[5];
+}
diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h
new file mode 100644
index 00000000..07436379
--- /dev/null
+++ b/src/systemd/src/basic/ether-addr-util.h
@@ -0,0 +1,37 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/ethernet.h>
+#include <stdbool.h>
+
+#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
+#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
+
+#define ETHER_ADDR_TO_STRING_MAX (3*6)
+char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
+
+bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b);
+
+#define ETHER_ADDR_NULL ((const struct ether_addr){})
+
+static inline bool ether_addr_is_null(const struct ether_addr *addr) {
+        return ether_addr_equal(addr, &ETHER_ADDR_NULL);
+}
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
index 9b091213..e940e10e 100644
--- a/src/systemd/src/basic/fileio.c
+++ b/src/systemd/src/basic/fileio.c
@@ -42,9 +42,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "random-util.h"
-#if 0 /* NM_IGNORED */
 #include "stdio-util.h"
-#endif /* NM_IGNORED */
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
@@ -356,7 +354,7 @@ static int parse_env_file_internal(
                 case KEY:
                         if (strchr(newline, c)) {
                                 state = PRE_KEY;
-                                line ++;
+                                line++;
                                 n_key = 0;
                         } else if (c == '=') {
                                 state = PRE_VALUE;
@@ -380,7 +378,7 @@ static int parse_env_file_internal(
                 case PRE_VALUE:
                         if (strchr(newline, c)) {
                                 state = PRE_KEY;
-                                line ++;
+                                line++;
                                 key[n_key] = 0;
 
                                 if (value)
@@ -420,7 +418,7 @@ static int parse_env_file_internal(
                 case VALUE:
                         if (strchr(newline, c)) {
                                 state = PRE_KEY;
-                                line ++;
+                                line++;
 
                                 key[n_key] = 0;
 
@@ -539,7 +537,7 @@ static int parse_env_file_internal(
                                 state = COMMENT_ESCAPE;
                         else if (strchr(newline, c)) {
                                 state = PRE_KEY;
-                                line ++;
+                                line++;
                         }
                         break;
 
@@ -912,7 +910,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
                 /* Back off one char if there's nothing but whitespace
                    and zeros */
                 if (!*t || isspace(*t))
-                        t --;
+                        t--;
         }
 
         len = strcspn(t, terminator);
diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c
index a3306e3d..9a843644 100644
--- a/src/systemd/src/basic/fs-util.c
+++ b/src/systemd/src/basic/fs-util.c
@@ -291,24 +291,6 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
 
         return 0;
 }
-
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
-        assert(fd >= 0);
-
-        /* Under the assumption that we are running privileged we
-         * first change the access mode and only then hand out
-         * ownership to avoid a window where access is too open. */
-
-        if (mode != MODE_INVALID)
-                if (fchmod(fd, mode) < 0)
-                        return -errno;
-
-        if (uid != UID_INVALID || gid != GID_INVALID)
-                if (fchown(fd, uid, gid) < 0)
-                        return -errno;
-
-        return 0;
-}
 #endif /* NM_IGNORED */
 
 int fchmod_umask(int fd, mode_t m) {
diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h
index 0e2fcb21..0d23f863 100644
--- a/src/systemd/src/basic/fs-util.h
+++ b/src/systemd/src/basic/fs-util.h
@@ -43,7 +43,6 @@ int readlink_and_canonicalize(const char *p, char **r);
 int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
 
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
 
 int fchmod_umask(int fd, mode_t mode);
 
diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c
index 85071d3e..4df3ae73 100644
--- a/src/systemd/src/basic/hashmap.c
+++ b/src/systemd/src/basic/hashmap.c
@@ -180,7 +180,7 @@ enum HashmapType {
 };
 
 struct _packed_ indirect_storage {
-        char    *storage;                  /* where buckets and DIBs are stored */
+        void *storage;                     /* where buckets and DIBs are stored */
         uint8_t  hash_key[HASH_KEY_SIZE];  /* hash key; changes during resize */
 
         unsigned n_entries;                /* number of stored entries */
@@ -197,7 +197,7 @@ 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. */
-        char storage[sizeof(struct indirect_storage)];
+        uint8_t storage[sizeof(struct indirect_storage)];
 };
 
 #define DIRECT_BUCKETS(entry_t) \
@@ -306,7 +306,7 @@ static void n_entries_dec(HashmapBase *h) {
                 h->n_direct_entries--;
 }
 
-static char *storage_ptr(HashmapBase *h) {
+static void *storage_ptr(HashmapBase *h) {
         return h->has_indirect ? h->indirect.storage
                                : h->direct.storage;
 }
@@ -351,7 +351,7 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
 
 static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
         return (struct hashmap_base_entry*)
-                (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+                ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
 }
 
 static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
@@ -385,7 +385,7 @@ static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_
 
 static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
         return (dib_raw_t*)
-                (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
+                ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
 }
 
 static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
@@ -1032,7 +1032,7 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
  */
 static int resize_buckets(HashmapBase *h, unsigned entries_add) {
         struct swap_entries swap;
-        char *new_storage;
+        void *new_storage;
         dib_raw_t *old_dibs, *new_dibs;
         const struct hashmap_type_info *hi;
         unsigned idx, optimal_idx;
@@ -1099,7 +1099,7 @@ static int resize_buckets(HashmapBase *h, unsigned entries_add) {
         h->indirect.n_buckets = (1U << new_shift) /
                                 (hi->entry_size + sizeof(dib_raw_t));
 
-        old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
+        old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets);
         new_dibs = dib_raw_ptr(h);
 
         /*
diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c
index 99eaa7a2..950e3230 100644
--- a/src/systemd/src/basic/hexdecoct.c
+++ b/src/systemd/src/basic/hexdecoct.c
@@ -27,6 +27,7 @@
 #include "alloc-util.h"
 #include "hexdecoct.h"
 #include "macro.h"
+#include "util.h"
 
 char octchar(int x) {
         return '0' + (x & 7);
@@ -277,8 +278,8 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
         if (padding) {
                 /* strip the padding */
                 while (l > 0 && p[l - 1] == '=' && pad < 7) {
-                        pad ++;
-                        l --;
+                        pad++;
+                        l--;
                 }
         }
 
@@ -506,7 +507,7 @@ int unbase64char(char c) {
         if (c == '+')
                 return offset;
 
-        offset ++;
+        offset++;
 
         if (c == '/')
                 return offset;
@@ -574,7 +575,7 @@ static int base64_append_width(char **prefix, int plen,
         if (!t)
                 return -ENOMEM;
 
-        memcpy(t + plen, sep, slen);
+        memcpy_safe(t + plen, sep, slen);
 
         for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
                 int act = MIN(width, avail);
@@ -622,9 +623,9 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
 
         /* strip the padding */
         if (l > 0 && p[l - 1] == '=')
-                l --;
+                l--;
         if (l > 0 && p[l - 1] == '=')
-                l --;
+                l--;
 
         /* a group of four input bytes needs three output bytes, in case of
            padding we need to add two or three extra bytes */
diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c
index 5875f4b4..ee48b86d 100644
--- a/src/systemd/src/basic/hostname-util.c
+++ b/src/systemd/src/basic/hostname-util.c
@@ -19,7 +19,6 @@
 
 #include "nm-sd-adapt.h"
 
-#include <bits/local_lim.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
@@ -51,6 +50,10 @@ bool hostname_is_set(void) {
 char* gethostname_malloc(void) {
         struct utsname u;
 
+        /* This call tries to return something useful, either the actual hostname
+         * or it makes something up. The only reason it might fail is OOM.
+         * It might even return "localhost" if that's set. */
+
         assert_se(uname(&u) >= 0);
 
         if (isempty(u.nodename) || streq(u.nodename, "(none)"))
@@ -59,6 +62,31 @@ char* gethostname_malloc(void) {
         return strdup(u.nodename);
 }
 
+int gethostname_strict(char **ret) {
+        struct utsname u;
+        char *k;
+
+        /* This call will rather fail than make up a name. It will not return "localhost" either. */
+
+        assert_se(uname(&u) >= 0);
+
+        if (isempty(u.nodename))
+                return -ENXIO;
+
+        if (streq(u.nodename, "(none)"))
+                return -ENXIO;
+
+        if (is_localhost(u.nodename))
+                return -ENXIO;
+
+        k = strdup(u.nodename);
+        if (!k)
+                return -ENOMEM;
+
+        *ret = k;
+        return 0;
+}
+
 static bool hostname_valid_char(char c) {
         return
                 (c >= 'a' && c <= 'z') ||
@@ -98,7 +126,7 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
                                 return false;
 
                         dot = true;
-                        n_dots ++;
+                        n_dots++;
                 } else {
                         if (!hostname_valid_char(*p))
                                 return false;
@@ -124,6 +152,8 @@ char* hostname_cleanup(char *s) {
 
         assert(s);
 
+        strshorten(s, HOST_NAME_MAX);
+
         for (p = s, d = s, dot = true; *p; p++) {
                 if (*p == '.') {
                         if (dot)
@@ -143,8 +173,6 @@ char* hostname_cleanup(char *s) {
         else
                 *d = 0;
 
-        strshorten(s, HOST_NAME_MAX);
-
         return s;
 }
 
diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h
index d062edde..7af4e6c7 100644
--- a/src/systemd/src/basic/hostname-util.h
+++ b/src/systemd/src/basic/hostname-util.h
@@ -26,6 +26,7 @@
 bool hostname_is_set(void);
 
 char* gethostname_malloc(void);
+int gethostname_strict(char **ret);
 
 bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
 char* hostname_cleanup(char *s);
diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c
index 1b2fc99f..aaf414f4 100644
--- a/src/systemd/src/basic/io-util.c
+++ b/src/systemd/src/basic/io-util.c
@@ -251,7 +251,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
                 } else if (n > 0)
                         q += n;
                 else
-                        q ++;
+                        q++;
         }
 
         if (q > w) {
diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h
index 142c940d..4684ed3b 100644
--- a/src/systemd/src/basic/io-util.h
+++ b/src/systemd/src/basic/io-util.h
@@ -46,7 +46,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
                 char *_s = (char *)(s);         \
                 _i->iov_base = _s;              \
                 _i->iov_len = strlen(_s);       \
-        } while(false)
+        } while (false)
 
 static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) {
         unsigned j;
diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h
index c68185f5..5962aa42 100644
--- a/src/systemd/src/basic/list.h
+++ b/src/systemd/src/basic/list.h
@@ -32,7 +32,7 @@
 #define LIST_HEAD_INIT(head)                                            \
         do {                                                            \
                 (head) = NULL; }                                        \
-        while(false)
+        while (false)
 
 /* Initialize a list item */
 #define LIST_INIT(name,item)                                            \
@@ -40,7 +40,7 @@
                 typeof(*(item)) *_item = (item);                        \
                 assert(_item);                                          \
                 _item->name##_prev = _item->name##_next = NULL;         \
-        } while(false)
+        } while (false)
 
 /* Prepend an item to the list */
 #define LIST_PREPEND(name,head,item)                                    \
@@ -51,7 +51,7 @@
                         _item->name##_next->name##_prev = _item;        \
                 _item->name##_prev = NULL;                              \
                 *_head = _item;                                         \
-        } while(false)
+        } while (false)
 
 /* Append an item to the list */
 #define LIST_APPEND(name,head,item)                                     \
@@ -59,7 +59,7 @@
                 typeof(*(head)) *_tail;                                 \
                 LIST_FIND_TAIL(name,head,_tail);                        \
                 LIST_INSERT_AFTER(name,head,_tail,item);                \
-        } while(false)
+        } while (false)
 
 /* Remove an item from the list */
 #define LIST_REMOVE(name,head,item)                                     \
@@ -75,7 +75,7 @@
                         *_head = _item->name##_next;                    \
                 }                                                       \
                 _item->name##_next = _item->name##_prev = NULL;         \
-        } while(false)
+        } while (false)
 
 /* Find the head of the list */
 #define LIST_FIND_HEAD(name,item,head)                                  \
@@ -119,7 +119,7 @@
                         _b->name##_prev = _a;                           \
                         _a->name##_next = _b;                           \
                 }                                                       \
-        } while(false)
+        } while (false)
 
 /* Insert an item before another one (a = where, b = what) */
 #define LIST_INSERT_BEFORE(name,head,a,b)                               \
@@ -145,7 +145,7 @@
                         _b->name##_next = _a;                           \
                         _a->name##_prev = _b;                           \
                 }                                                       \
-        } while(false)
+        } while (false)
 
 #define LIST_JUST_US(name,item)                                         \
         (!(item)->name##_prev && !(item)->name##_next)                  \
diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h
index d0ec4dfa..322ad6ca 100644
--- a/src/systemd/src/basic/log.h
+++ b/src/systemd/src/basic/log.h
@@ -195,7 +195,7 @@ void log_assert_failed_return(
 #ifdef LOG_TRACE
 #  define log_trace(...) log_debug(__VA_ARGS__)
 #else
-#  define log_trace(...) do {} while(0)
+#  define log_trace(...) do {} while (0)
 #endif
 
 /* Structured logging */
@@ -248,5 +248,4 @@ int log_syntax_internal(
                         log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
                                             "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
                 }                                                       \
-                -EINVAL;                                                \
         })
diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h
index d12e09a7..d6a877ea 100644
--- a/src/systemd/src/basic/macro.h
+++ b/src/systemd/src/basic/macro.h
@@ -23,10 +23,15 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <sys/param.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 
 #define _printf_(a,b) __attribute__ ((format (printf, a, b)))
-#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
+#ifdef __clang__
+#  define _alloc_(...)
+#else
+#  define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
+#endif
 #define _sentinel_ __attribute__ ((sentinel))
 #define _unused_ __attribute__ ((unused))
 #define _destructor_ __attribute__ ((destructor))
@@ -233,7 +238,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
 /* We override the glibc assert() here. */
 #undef assert
 #ifdef NDEBUG
-#define assert(expr) do {} while(false)
+#define assert(expr) do {} while (false)
 #else
 #define assert(expr) assert_message_se(expr, #expr)
 #endif
@@ -370,6 +375,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
                 _found;                         \
         })
 
+#define SWAP_TWO(x, y) do {                        \
+                typeof(x) _t = (x);                \
+                (x) = (y);                         \
+                (y) = (_t);                        \
+        } while (false)
+
 /* Define C11 thread_local attribute even on older gcc compiler
  * version */
 #ifndef thread_local
diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h
index fea7841b..0618b8dd 100644
--- a/src/systemd/src/basic/mempool.h
+++ b/src/systemd/src/basic/mempool.h
@@ -36,7 +36,7 @@ void* mempool_alloc0_tile(struct mempool *mp);
 void mempool_free_tile(struct mempool *mp, void *p);
 
 #define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
-struct mempool pool_name = { \
+static struct mempool pool_name = { \
         .tile_size = sizeof(tile_type), \
         .at_least = alloc_at_least, \
 }
diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c
index beb90fa6..eb53c42d 100644
--- a/src/systemd/src/basic/parse-util.c
+++ b/src/systemd/src/basic/parse-util.c
@@ -511,7 +511,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
         s = *p;
 
         /* accept any number of digits, strtoull is limted to 19 */
-        for(i=0; i < digits; i++,s++) {
+        for (i=0; i < digits; i++,s++) {
                 if (*s < '0' || *s > '9') {
                         if (i == 0)
                                 return -EINVAL;
diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h
index 2bff5062..e0d9dd00 100644
--- a/src/systemd/src/basic/set.h
+++ b/src/systemd/src/basic/set.h
@@ -126,6 +126,9 @@ int set_put_strdupv(Set *s, char **l);
 #define SET_FOREACH(e, s, i) \
         for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
 
+#define SET_FOREACH_MOVE(e, d, s)                                       \
+        for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); )
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
 
diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c
new file mode 100644
index 00000000..c68db7c3
--- /dev/null
+++ b/src/systemd/src/basic/socket-util.c
@@ -0,0 +1,982 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/ip.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#if 0 /* NM_IGNORED */
+#include "formats-util.h"
+#endif /* NM_IGNORED */
+#include "log.h"
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif /* NM_IGNORED */
+#include "parse-util.h"
+#include "path-util.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#if 0 /* NM_IGNORED */
+#include "user-util.h"
+#endif /* NM_IGNORED */
+#include "util.h"
+
+#if 0 /* NM_IGNORED */
+int socket_address_parse(SocketAddress *a, const char *s) {
+        char *e, *n;
+        unsigned u;
+        int r;
+
+        assert(a);
+        assert(s);
+
+        zero(*a);
+        a->type = SOCK_STREAM;
+
+        if (*s == '[') {
+                /* IPv6 in [x:.....:z]:p notation */
+
+                e = strchr(s+1, ']');
+                if (!e)
+                        return -EINVAL;
+
+                n = strndupa(s+1, e-s-1);
+
+                errno = 0;
+                if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
+                        return errno > 0 ? -errno : -EINVAL;
+
+                e++;
+                if (*e != ':')
+                        return -EINVAL;
+
+                e++;
+                r = safe_atou(e, &u);
+                if (r < 0)
+                        return r;
+
+                if (u <= 0 || u > 0xFFFF)
+                        return -EINVAL;
+
+                a->sockaddr.in6.sin6_family = AF_INET6;
+                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                a->size = sizeof(struct sockaddr_in6);
+
+        } else if (*s == '/') {
+                /* AF_UNIX socket */
+
+                size_t l;
+
+                l = strlen(s);
+                if (l >= sizeof(a->sockaddr.un.sun_path))
+                        return -EINVAL;
+
+                a->sockaddr.un.sun_family = AF_UNIX;
+                memcpy(a->sockaddr.un.sun_path, s, l);
+                a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+
+        } else if (*s == '@') {
+                /* Abstract AF_UNIX socket */
+                size_t l;
+
+                l = strlen(s+1);
+                if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+                        return -EINVAL;
+
+                a->sockaddr.un.sun_family = AF_UNIX;
+                memcpy(a->sockaddr.un.sun_path+1, s+1, l);
+                a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+
+        } else {
+                e = strchr(s, ':');
+                if (e) {
+                        r = safe_atou(e+1, &u);
+                        if (r < 0)
+                                return r;
+
+                        if (u <= 0 || u > 0xFFFF)
+                                return -EINVAL;
+
+                        n = strndupa(s, e-s);
+
+                        /* IPv4 in w.x.y.z:p notation? */
+                        r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
+                        if (r < 0)
+                                return -errno;
+
+                        if (r > 0) {
+                                /* Gotcha, it's a traditional IPv4 address */
+                                a->sockaddr.in.sin_family = AF_INET;
+                                a->sockaddr.in.sin_port = htons((uint16_t) u);
+                                a->size = sizeof(struct sockaddr_in);
+                        } else {
+                                unsigned idx;
+
+                                if (strlen(n) > IF_NAMESIZE-1)
+                                        return -EINVAL;
+
+                                /* Uh, our last resort, an interface name */
+                                idx = if_nametoindex(n);
+                                if (idx == 0)
+                                        return -EINVAL;
+
+                                a->sockaddr.in6.sin6_family = AF_INET6;
+                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                a->sockaddr.in6.sin6_scope_id = idx;
+                                a->sockaddr.in6.sin6_addr = in6addr_any;
+                                a->size = sizeof(struct sockaddr_in6);
+                        }
+                } else {
+
+                        /* Just a port */
+                        r = safe_atou(s, &u);
+                        if (r < 0)
+                                return r;
+
+                        if (u <= 0 || u > 0xFFFF)
+                                return -EINVAL;
+
+                        if (socket_ipv6_is_supported()) {
+                                a->sockaddr.in6.sin6_family = AF_INET6;
+                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                a->sockaddr.in6.sin6_addr = in6addr_any;
+                                a->size = sizeof(struct sockaddr_in6);
+                        } else {
+                                a->sockaddr.in.sin_family = AF_INET;
+                                a->sockaddr.in.sin_port = htons((uint16_t) u);
+                                a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
+                                a->size = sizeof(struct sockaddr_in);
+                        }
+                }
+        }
+
+        return 0;
+}
+
+int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
+        SocketAddress b;
+        int r;
+
+        /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
+
+        r = socket_address_parse(&b, s);
+        if (r < 0)
+                return r;
+
+        if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
+                log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+                return -EAFNOSUPPORT;
+        }
+
+        *a = b;
+        return 0;
+}
+
+int socket_address_parse_netlink(SocketAddress *a, const char *s) {
+        int family;
+        unsigned group = 0;
+        _cleanup_free_ char *sfamily = NULL;
+        assert(a);
+        assert(s);
+
+        zero(*a);
+        a->type = SOCK_RAW;
+
+        errno = 0;
+        if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
+                return errno > 0 ? -errno : -EINVAL;
+
+        family = netlink_family_from_string(sfamily);
+        if (family < 0)
+                return -EINVAL;
+
+        a->sockaddr.nl.nl_family = AF_NETLINK;
+        a->sockaddr.nl.nl_groups = group;
+
+        a->type = SOCK_RAW;
+        a->size = sizeof(struct sockaddr_nl);
+        a->protocol = family;
+
+        return 0;
+}
+
+int socket_address_verify(const SocketAddress *a) {
+        assert(a);
+
+        switch (socket_address_family(a)) {
+
+        case AF_INET:
+                if (a->size != sizeof(struct sockaddr_in))
+                        return -EINVAL;
+
+                if (a->sockaddr.in.sin_port == 0)
+                        return -EINVAL;
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
+        case AF_INET6:
+                if (a->size != sizeof(struct sockaddr_in6))
+                        return -EINVAL;
+
+                if (a->sockaddr.in6.sin6_port == 0)
+                        return -EINVAL;
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
+        case AF_UNIX:
+                if (a->size < offsetof(struct sockaddr_un, sun_path))
+                        return -EINVAL;
+
+                if (a->size > offsetof(struct sockaddr_un, sun_path)) {
+
+                        if (a->sockaddr.un.sun_path[0] != 0) {
+                                char *e;
+
+                                /* path */
+                                e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
+                                if (!e)
+                                        return -EINVAL;
+
+                                if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
+                                        return -EINVAL;
+                        }
+                }
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
+                        return -EINVAL;
+
+                return 0;
+
+        case AF_NETLINK:
+
+                if (a->size != sizeof(struct sockaddr_nl))
+                        return -EINVAL;
+
+                if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_address_print(const SocketAddress *a, char **ret) {
+        int r;
+
+        assert(a);
+        assert(ret);
+
+        r = socket_address_verify(a);
+        if (r < 0)
+                return r;
+
+        if (socket_address_family(a) == AF_NETLINK) {
+                _cleanup_free_ char *sfamily = NULL;
+
+                r = netlink_family_to_string_alloc(a->protocol, &sfamily);
+                if (r < 0)
+                        return r;
+
+                r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
+                if (r < 0)
+                        return -ENOMEM;
+
+                return 0;
+        }
+
+        return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
+}
+
+bool socket_address_can_accept(const SocketAddress *a) {
+        assert(a);
+
+        return
+                a->type == SOCK_STREAM ||
+                a->type == SOCK_SEQPACKET;
+}
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
+        assert(a);
+        assert(b);
+
+        /* Invalid addresses are unequal to all */
+        if (socket_address_verify(a) < 0 ||
+            socket_address_verify(b) < 0)
+                return false;
+
+        if (a->type != b->type)
+                return false;
+
+        if (socket_address_family(a) != socket_address_family(b))
+                return false;
+
+        switch (socket_address_family(a)) {
+
+        case AF_INET:
+                if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
+                        return false;
+
+                if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
+                        return false;
+
+                break;
+
+        case AF_INET6:
+                if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
+                        return false;
+
+                if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
+                        return false;
+
+                break;
+
+        case AF_UNIX:
+                if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
+                    b->size <= offsetof(struct sockaddr_un, sun_path))
+                        return false;
+
+                if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
+                        return false;
+
+                if (a->sockaddr.un.sun_path[0]) {
+                        if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
+                                return false;
+                } else {
+                        if (a->size != b->size)
+                                return false;
+
+                        if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
+                                return false;
+                }
+
+                break;
+
+        case AF_NETLINK:
+                if (a->protocol != b->protocol)
+                        return false;
+
+                if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
+                        return false;
+
+                break;
+
+        default:
+                /* Cannot compare, so we assume the addresses are different */
+                return false;
+        }
+
+        return true;
+}
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type) {
+        struct SocketAddress b;
+
+        assert(a);
+        assert(s);
+
+        if (socket_address_parse(&b, s) < 0)
+                return false;
+
+        b.type = type;
+
+        return socket_address_equal(a, &b);
+}
+
+bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
+        struct SocketAddress b;
+
+        assert(a);
+        assert(s);
+
+        if (socket_address_parse_netlink(&b, s) < 0)
+                return false;
+
+        return socket_address_equal(a, &b);
+}
+
+const char* socket_address_get_path(const SocketAddress *a) {
+        assert(a);
+
+        if (socket_address_family(a) != AF_UNIX)
+                return NULL;
+
+        if (a->sockaddr.un.sun_path[0] == 0)
+                return NULL;
+
+        return a->sockaddr.un.sun_path;
+}
+
+bool socket_ipv6_is_supported(void) {
+        if (access("/proc/net/sockstat6", F_OK) != 0)
+                return false;
+
+        return true;
+}
+
+bool socket_address_matches_fd(const SocketAddress *a, int fd) {
+        SocketAddress b;
+        socklen_t solen;
+
+        assert(a);
+        assert(fd >= 0);
+
+        b.size = sizeof(b.sockaddr);
+        if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
+                return false;
+
+        if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
+                return false;
+
+        solen = sizeof(b.type);
+        if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
+                return false;
+
+        if (b.type != a->type)
+                return false;
+
+        if (a->protocol != 0)  {
+                solen = sizeof(b.protocol);
+                if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
+                        return false;
+
+                if (b.protocol != a->protocol)
+                        return false;
+        }
+
+        return socket_address_equal(a, &b);
+}
+
+int sockaddr_port(const struct sockaddr *_sa) {
+        union sockaddr_union *sa = (union sockaddr_union*) _sa;
+
+        assert(sa);
+
+        if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
+                return -EAFNOSUPPORT;
+
+        return ntohs(sa->sa.sa_family == AF_INET6 ?
+                       sa->in6.sin6_port :
+                       sa->in.sin_port);
+}
+
+int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
+        union sockaddr_union *sa = (union sockaddr_union*) _sa;
+        char *p;
+        int r;
+
+        assert(sa);
+        assert(salen >= sizeof(sa->sa.sa_family));
+
+        switch (sa->sa.sa_family) {
+
+        case AF_INET: {
+                uint32_t a;
+
+                a = ntohl(sa->in.sin_addr.s_addr);
+
+                if (include_port)
+                        r = asprintf(&p,
+                                     "%u.%u.%u.%u:%u",
+                                     a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
+                                     ntohs(sa->in.sin_port));
+                else
+                        r = asprintf(&p,
+                                     "%u.%u.%u.%u",
+                                     a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
+                if (r < 0)
+                        return -ENOMEM;
+                break;
+        }
+
+        case AF_INET6: {
+                static const unsigned char ipv4_prefix[] = {
+                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
+                };
+
+                if (translate_ipv6 &&
+                    memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
+                        const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
+                        if (include_port)
+                                r = asprintf(&p,
+                                             "%u.%u.%u.%u:%u",
+                                             a[0], a[1], a[2], a[3],
+                                             ntohs(sa->in6.sin6_port));
+                        else
+                                r = asprintf(&p,
+                                             "%u.%u.%u.%u",
+                                             a[0], a[1], a[2], a[3]);
+                        if (r < 0)
+                                return -ENOMEM;
+                } else {
+                        char a[INET6_ADDRSTRLEN];
+
+                        inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+
+                        if (include_port) {
+                                r = asprintf(&p,
+                                             "[%s]:%u",
+                                             a,
+                                             ntohs(sa->in6.sin6_port));
+                                if (r < 0)
+                                        return -ENOMEM;
+                        } else {
+                                p = strdup(a);
+                                if (!p)
+                                        return -ENOMEM;
+                        }
+                }
+
+                break;
+        }
+
+        case AF_UNIX:
+                if (salen <= offsetof(struct sockaddr_un, sun_path)) {
+                        p = strdup("<unnamed>");
+                        if (!p)
+                                return -ENOMEM;
+
+                } else if (sa->un.sun_path[0] == 0) {
+                        /* abstract */
+
+                        /* FIXME: We assume we can print the
+                         * socket path here and that it hasn't
+                         * more than one NUL byte. That is
+                         * actually an invalid assumption */
+
+                        p = new(char, sizeof(sa->un.sun_path)+1);
+                        if (!p)
+                                return -ENOMEM;
+
+                        p[0] = '@';
+                        memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
+                        p[sizeof(sa->un.sun_path)] = 0;
+
+                } else {
+                        p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
+                        if (!p)
+                                return -ENOMEM;
+                }
+
+                break;
+
+        default:
+                return -EOPNOTSUPP;
+        }
+
+
+        *ret = p;
+        return 0;
+}
+
+int getpeername_pretty(int fd, bool include_port, char **ret) {
+        union sockaddr_union sa;
+        socklen_t salen = sizeof(sa);
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (getpeername(fd, &sa.sa, &salen) < 0)
+                return -errno;
+
+        if (sa.sa.sa_family == AF_UNIX) {
+                struct ucred ucred = {};
+
+                /* UNIX connection sockets are anonymous, so let's use
+                 * PID/UID as pretty credentials instead */
+
+                r = getpeercred(fd, &ucred);
+                if (r < 0)
+                        return r;
+
+                if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
+                        return -ENOMEM;
+
+                return 0;
+        }
+
+        /* For remote sockets we translate IPv6 addresses back to IPv4
+         * if applicable, since that's nicer. */
+
+        return sockaddr_pretty(&sa.sa, salen, true, include_port, ret);
+}
+
+int getsockname_pretty(int fd, char **ret) {
+        union sockaddr_union sa;
+        socklen_t salen = sizeof(sa);
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (getsockname(fd, &sa.sa, &salen) < 0)
+                return -errno;
+
+        /* For local sockets we do not translate IPv6 addresses back
+         * to IPv6 if applicable, since this is usually used for
+         * listening sockets where the difference between IPv4 and
+         * IPv6 matters. */
+
+        return sockaddr_pretty(&sa.sa, salen, false, true, ret);
+}
+
+int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
+        int r;
+        char host[NI_MAXHOST], *ret;
+
+        assert(_ret);
+
+        r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
+                        NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
+        if (r != 0) {
+                int saved_errno = errno;
+
+                r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
+                if (r < 0)
+                        return r;
+
+                log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
+        } else {
+                ret = strdup(host);
+                if (!ret)
+                        return -ENOMEM;
+        }
+
+        *_ret = ret;
+        return 0;
+}
+
+int getnameinfo_pretty(int fd, char **ret) {
+        union sockaddr_union sa;
+        socklen_t salen = sizeof(sa);
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (getsockname(fd, &sa.sa, &salen) < 0)
+                return -errno;
+
+        return socknameinfo_pretty(&sa, salen, ret);
+}
+
+int socket_address_unlink(SocketAddress *a) {
+        assert(a);
+
+        if (socket_address_family(a) != AF_UNIX)
+                return 0;
+
+        if (a->sockaddr.un.sun_path[0] == 0)
+                return 0;
+
+        if (unlink(a->sockaddr.un.sun_path) < 0)
+                return -errno;
+
+        return 1;
+}
+
+static const char* const netlink_family_table[] = {
+        [NETLINK_ROUTE] = "route",
+        [NETLINK_FIREWALL] = "firewall",
+        [NETLINK_INET_DIAG] = "inet-diag",
+        [NETLINK_NFLOG] = "nflog",
+        [NETLINK_XFRM] = "xfrm",
+        [NETLINK_SELINUX] = "selinux",
+        [NETLINK_ISCSI] = "iscsi",
+        [NETLINK_AUDIT] = "audit",
+        [NETLINK_FIB_LOOKUP] = "fib-lookup",
+        [NETLINK_CONNECTOR] = "connector",
+        [NETLINK_NETFILTER] = "netfilter",
+        [NETLINK_IP6_FW] = "ip6-fw",
+        [NETLINK_DNRTMSG] = "dnrtmsg",
+        [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
+        [NETLINK_GENERIC] = "generic",
+        [NETLINK_SCSITRANSPORT] = "scsitransport",
+        [NETLINK_ECRYPTFS] = "ecryptfs"
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
+
+static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
+        [SOCKET_ADDRESS_DEFAULT] = "default",
+        [SOCKET_ADDRESS_BOTH] = "both",
+        [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+
+bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
+        assert(a);
+        assert(b);
+
+        if (a->sa.sa_family != b->sa.sa_family)
+                return false;
+
+        if (a->sa.sa_family == AF_INET)
+                return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
+
+        if (a->sa.sa_family == AF_INET6)
+                return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
+
+        return false;
+}
+
+int fd_inc_sndbuf(int fd, size_t n) {
+        int r, value;
+        socklen_t l = sizeof(value);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+                return 0;
+
+        /* If we have the privileges we will ignore the kernel limit. */
+
+        value = (int) n;
+        if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+                        return -errno;
+
+        return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+        int r, value;
+        socklen_t l = sizeof(value);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+                return 0;
+
+        /* If we have the privileges we will ignore the kernel limit. */
+
+        value = (int) n;
+        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+                        return -errno;
+        return 1;
+}
+
+static const char* const ip_tos_table[] = {
+        [IPTOS_LOWDELAY] = "low-delay",
+        [IPTOS_THROUGHPUT] = "throughput",
+        [IPTOS_RELIABILITY] = "reliability",
+        [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+
+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)
+                return -errno;
+
+        if (n != sizeof(struct ucred))
+                return -EIO;
+
+        /* Check if the data is actually useful and not suppressed due
+         * to namespacing issues */
+        if (u.pid <= 0)
+                return -ENODATA;
+        if (u.uid == UID_INVALID)
+                return -ENODATA;
+        if (u.gid == GID_INVALID)
+                return -ENODATA;
+
+        *ucred = u;
+        return 0;
+}
+
+int getpeersec(int fd, char **ret) {
+        socklen_t n = 64;
+        char *s;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        s = new0(char, n);
+        if (!s)
+                return -ENOMEM;
+
+        r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+        if (r < 0) {
+                free(s);
+
+                if (errno != ERANGE)
+                        return -errno;
+
+                s = new0(char, n);
+                if (!s)
+                        return -ENOMEM;
+
+                r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
+                if (r < 0) {
+                        free(s);
+                        return -errno;
+                }
+        }
+
+        if (isempty(s)) {
+                free(s);
+                return -EOPNOTSUPP;
+        }
+
+        *ret = s;
+        return 0;
+}
+
+int send_one_fd_sa(
+                int transport_fd,
+                int fd,
+                const struct sockaddr *sa, socklen_t len,
+                int flags) {
+
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(int))];
+        } control = {};
+        struct msghdr mh = {
+                .msg_name = (struct sockaddr*) sa,
+                .msg_namelen = len,
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg;
+
+        assert(transport_fd >= 0);
+        assert(fd >= 0);
+
+        cmsg = CMSG_FIRSTHDR(&mh);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+        mh.msg_controllen = CMSG_SPACE(sizeof(int));
+        if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int receive_one_fd(int transport_fd, int flags) {
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(int))];
+        } control = {};
+        struct msghdr mh = {
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg, *found = NULL;
+
+        assert(transport_fd >= 0);
+
+        /*
+         * Receive a single FD via @transport_fd. We don't care for
+         * the transport-type. We retrieve a single FD at most, so for
+         * packet-based transports, the caller must ensure to send
+         * only a single FD per packet.  This is best used in
+         * combination with send_one_fd().
+         */
+
+        if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
+                return -errno;
+
+        CMSG_FOREACH(cmsg, &mh) {
+                if (cmsg->cmsg_level == SOL_SOCKET &&
+                    cmsg->cmsg_type == SCM_RIGHTS &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+                        assert(!found);
+                        found = cmsg;
+                        break;
+                }
+        }
+
+        if (!found) {
+                cmsg_close_all(&mh);
+                return -EIO;
+        }
+
+        return *(int*) CMSG_DATA(found);
+}
+#endif /* NM_IGNORED */
+
+ssize_t next_datagram_size_fd(int fd) {
+        ssize_t l;
+        int k;
+
+        /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will
+         * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't
+         * do. This difference is actually of major importance as we need to be sure that the size returned here
+         * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of
+         * the wrong size. */
+
+        l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC);
+        if (l < 0) {
+                if (errno == EOPNOTSUPP)
+                        goto fallback;
+
+                return -errno;
+        }
+        if (l == 0)
+                goto fallback;
+
+        return l;
+
+fallback:
+        k = 0;
+
+        /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD
+         * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */
+
+        if (ioctl(fd, FIONREAD, &k) < 0)
+                return -errno;
+
+        return (ssize_t) k;
+}
diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h
index 92edc1dc..d17a2f35 100644
--- a/src/systemd/src/basic/socket-util.h
+++ b/src/systemd/src/basic/socket-util.h
@@ -133,5 +133,7 @@ int send_one_fd_sa(int transport_fd,
 #define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags)
 int receive_one_fd(int transport_fd, int flags);
 
+ssize_t next_datagram_size_fd(int fd);
+
 #define CMSG_FOREACH(cmsg, mh)                                          \
         for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
diff --git a/src/systemd/src/basic/stdio-util.h b/src/systemd/src/basic/stdio-util.h
index 0a675571..bd1144b4 100644
--- a/src/systemd/src/basic/stdio-util.h
+++ b/src/systemd/src/basic/stdio-util.h
@@ -73,4 +73,4 @@ do {                                                                    \
                         assert_not_reached("Unknown format string argument."); \
                 }                                                       \
         }                                                               \
-} while(false)
+} while (false)
diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c
index 75df2121..68de772e 100644
--- a/src/systemd/src/basic/string-util.c
+++ b/src/systemd/src/basic/string-util.c
@@ -482,7 +482,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
         }
 
         if (k > x) /* last character was wide and went over quota */
-                x ++;
+                x++;
 
         for (j = s + old_length; k < new_length && j > i; ) {
                 char32_t c;
diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c
index c003091b..e885c598 100644
--- a/src/systemd/src/basic/strv.c
+++ b/src/systemd/src/basic/strv.c
@@ -377,7 +377,7 @@ char *strv_join(char **l, const char *separator) {
 
         n = 0;
         STRV_FOREACH(s, l) {
-                if (n != 0)
+                if (s != l)
                         n += k;
                 n += strlen(*s);
         }
@@ -388,7 +388,7 @@ char *strv_join(char **l, const char *separator) {
 
         e = r;
         STRV_FOREACH(s, l) {
-                if (e != r)
+                if (s != l)
                         e = stpcpy(e, separator);
 
                 e = stpcpy(e, *s);
diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c
index ed239be1..5b2b2180 100644
--- a/src/systemd/src/basic/time-util.c
+++ b/src/systemd/src/basic/time-util.c
@@ -44,10 +44,33 @@
 
 static nsec_t timespec_load_nsec(const struct timespec *ts);
 
+static clockid_t map_clock_id(clockid_t c) {
+
+        /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
+         * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
+         * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
+         * those archs.
+         *
+         * Also, older kernels don't support CLOCK_BOOTTIME: fall back to CLOCK_MONOTONIC. */
+
+        switch (c) {
+
+        case CLOCK_BOOTTIME:
+        case CLOCK_BOOTTIME_ALARM:
+                return clock_boottime_or_monotonic ();
+
+        case CLOCK_REALTIME_ALARM:
+                return CLOCK_REALTIME;
+
+        default:
+                return c;
+        }
+}
+
 usec_t now(clockid_t clock_id) {
         struct timespec ts;
 
-        assert_se(clock_gettime(clock_id, &ts) == 0);
+        assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
 
         return timespec_load(&ts);
 }
@@ -55,7 +78,7 @@ usec_t now(clockid_t clock_id) {
 nsec_t now_nsec(clockid_t clock_id) {
         struct timespec ts;
 
-        assert_se(clock_gettime(clock_id, &ts) == 0);
+        assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
 
         return timespec_load_nsec(&ts);
 }
@@ -121,8 +144,7 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
-        if (ts->tv_sec == (time_t) -1 &&
-            ts->tv_nsec == (long) -1)
+        if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
                 return USEC_INFINITY;
 
         if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
@@ -136,13 +158,13 @@ usec_t timespec_load(const struct timespec *ts) {
 static nsec_t timespec_load_nsec(const struct timespec *ts) {
         assert(ts);
 
-        if (ts->tv_sec == (time_t) -1 &&
-            ts->tv_nsec == (long) -1)
+        if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
                 return NSEC_INFINITY;
 
-        return
-                (nsec_t) ts->tv_sec * NSEC_PER_SEC +
-                (nsec_t) ts->tv_nsec;
+        if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
+                return NSEC_INFINITY;
+
+        return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
 }
 
 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
@@ -434,7 +456,7 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
         assert(t);
 
         if (sscanf(value, "%llu %llu", &a, &b) != 2) {
-                log_debug("Failed to parse finish timestamp value %s.", value);
+                log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
                 return -EINVAL;
         }
 
@@ -444,6 +466,18 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
         return 0;
 }
 
+int timestamp_deserialize(const char *value, usec_t *timestamp) {
+        int r;
+
+        assert(value);
+
+        r = safe_atou64(value, timestamp);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value);
+
+        return r;
+}
+
 int parse_timestamp(const char *t, usec_t *usec) {
         static const struct {
                 const char *name;
@@ -550,12 +584,12 @@ int parse_timestamp(const char *t, usec_t *usec) {
                 goto from_tm;
 
         } else if (streq(t, "yesterday")) {
-                tm.tm_mday --;
+                tm.tm_mday--;
                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
                 goto from_tm;
 
         } else if (streq(t, "tomorrow")) {
-                tm.tm_mday ++;
+                tm.tm_mday++;
                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
                 goto from_tm;
         }
@@ -678,8 +712,7 @@ finish:
         return 0;
 }
 
-int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
-
+static char* extract_multiplier(char *p, usec_t *multiplier) {
         static const struct {
                 const char *suffix;
                 usec_t usec;
@@ -713,7 +746,22 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
                 { "usec",    1ULL            },
                 { "us",      1ULL            },
         };
+        unsigned i;
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+                char *e;
 
+                e = startswith(p, table[i].suffix);
+                if (e) {
+                        *multiplier = table[i].usec;
+                        return e;
+                }
+        }
+
+        return p;
+}
+
+int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
         const char *p, *s;
         usec_t r = 0;
         bool something = false;
@@ -738,8 +786,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
         for (;;) {
                 long long l, z = 0;
                 char *e;
-                unsigned i, n = 0;
-                usec_t multiplier, k;
+                unsigned n = 0;
+                usec_t multiplier = default_unit, k;
 
                 p += strspn(p, WHITESPACE);
 
@@ -752,10 +800,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
 
                 errno = 0;
                 l = strtoll(p, &e, 10);
-
                 if (errno > 0)
                         return -errno;
-
                 if (l < 0)
                         return -ERANGE;
 
@@ -779,18 +825,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
                         return -EINVAL;
 
                 e += strspn(e, WHITESPACE);
-
-                for (i = 0; i < ELEMENTSOF(table); i++)
-                        if (startswith(e, table[i].suffix)) {
-                                multiplier = table[i].usec;
-                                p = e + strlen(table[i].suffix);
-                                break;
-                        }
-
-                if (i >= ELEMENTSOF(table)) {
-                        multiplier = default_unit;
-                        p = e;
-                }
+                p = extract_multiplier(e, &multiplier);
 
                 something = true;
 
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
index 9894e626..77e3cd08 100644
--- a/src/systemd/src/basic/time-util.h
+++ b/src/systemd/src/basic/time-util.h
@@ -99,6 +99,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
 
 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
 int dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+int timestamp_deserialize(const char *value, usec_t *timestamp);
 
 int parse_timestamp(const char *t, usec_t *usec);
 
diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c
index 5a1d3678..a6bdda6e 100644
--- a/src/systemd/src/basic/utf8.c
+++ b/src/systemd/src/basic/utf8.c
@@ -243,7 +243,7 @@ char *utf8_escape_non_printable(const char *str) {
                                         *(s++) = hexchar((int) *str);
 
                                         str += 1;
-                                        len --;
+                                        len--;
                                 }
                         }
                 } else {
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
index 5033b029..3225269a 100644
--- a/src/systemd/src/basic/util.h
+++ b/src/systemd/src/basic/util.h
@@ -36,6 +36,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/statfs.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
@@ -106,6 +107,16 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_
         qsort(base, nmemb, size, compar);
 }
 
+/**
+ * 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) {
+        if (n == 0)
+                return;
+        assert(src);
+        memcpy(dst, src, n);
+}
+
 int on_ac_power(void);
 
 #define memzero(x,l) (memset((x), 0, (l)))
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
index a3b842cd..4662b0d8 100644
--- a/src/systemd/src/libsystemd-network/dhcp-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -42,10 +42,10 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
 int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
                        uint8_t code, size_t optlen, const void *optval);
 
-typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
+typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
                                 const void *option, void *userdata);
 
-int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message);
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
 
 int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
                       uint8_t type, uint16_t arp_type, size_t optlen,
diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c
index 4f025b68..e003e088 100644
--- a/src/systemd/src/libsystemd-network/dhcp-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp-option.c
@@ -36,7 +36,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
 
         if (code != SD_DHCP_OPTION_END)
                 /* always make sure there is space for an END option */
-                size --;
+                size--;
 
         switch (code) {
 
@@ -56,12 +56,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
                 options[*offset] = code;
                 options[*offset + 1] = optlen;
 
-                if (optlen) {
-                        assert(optval);
-
-                        memcpy(&options[*offset + 2], optval, optlen);
-                }
-
+                memcpy_safe(&options[*offset + 2], optval, optlen);
                 *offset += optlen + 2;
 
                 break;
@@ -142,7 +137,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
 }
 
 static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
-                         uint8_t *message_type, char **error_message, dhcp_option_cb_t cb,
+                         uint8_t *message_type, char **error_message, dhcp_option_callback_t cb,
                          void *userdata) {
         uint8_t code, len;
         const uint8_t *option;
@@ -228,7 +223,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
         return 0;
 }
 
-int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) {
+int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) {
         _cleanup_free_ char *error_message = NULL;
         uint8_t overload = 0;
         uint8_t message_type = 0;
diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c
index 26dc2fa2..90d09912 100644
--- a/src/systemd/src/libsystemd-network/dhcp-packet.c
+++ b/src/systemd/src/libsystemd-network/dhcp-packet.c
@@ -68,7 +68,7 @@ uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) {
                         /* wrap around in one's complement */
                         sum++;
 
-                buf_64 ++;
+                buf_64++;
         }
 
         if (len % sizeof(uint64_t)) {
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index d4f726b5..ee63142c 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -73,8 +73,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
         if (r < 0)
                 return r;
 
-        if (optval)
-                memcpy(*buf, optval, optlen);
+        memcpy_safe(*buf, optval, optlen);
 
         *buf += optlen;
         *buflen -= optlen;
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.c b/src/systemd/src/libsystemd-network/lldp-internal.c
deleted file mode 100644
index 8194a601..00000000
--- a/src/systemd/src/libsystemd-network/lldp-internal.c
+++ /dev/null
@@ -1,362 +0,0 @@
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 Tom Gundersen
-  Copyright (C) 2014 Susant Sahani
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "nm-sd-adapt.h"
-
-#include "sd-lldp.h"
-
-#include "alloc-util.h"
-#include "lldp-internal.h"
-
-/* We store maximum 1K chassis entries */
-#define LLDP_MIB_MAX_CHASSIS 1024
-
-/* Maximum Ports can be attached to any chassis */
-#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
-
-/* 10.5.5.2.2 mibUpdateObjects ()
- * The mibUpdateObjects () procedure updates the MIB objects corresponding to
- * the TLVs contained in the received LLDPDU for the LLDP remote system
- * indicated by the LLDP remote systems update process defined in 10.3.5 */
-
-int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
-        lldp_neighbour_port *p;
-        uint16_t length, ttl;
-        uint8_t *data;
-        uint8_t type;
-        int r;
-
-        assert_return(c, -EINVAL);
-        assert_return(tlv, -EINVAL);
-
-        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
-        if (r < 0)
-                return r;
-
-        /* Update the packet if we already have */
-        LIST_FOREACH(port, p, c->ports) {
-
-                if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
-
-                        r = sd_lldp_packet_read_ttl(tlv, &ttl);
-                        if (r < 0)
-                                return r;
-
-                        p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
-
-                        sd_lldp_packet_unref(p->packet);
-                        p->packet = tlv;
-
-                        prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
-
-                        return 0;
-                }
-        }
-
-        return -1;
-}
-
-int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
-        lldp_neighbour_port *p, *q;
-        uint8_t *data;
-        uint16_t length;
-        uint8_t type;
-        int r;
-
-        assert_return(c, -EINVAL);
-        assert_return(tlv, -EINVAL);
-
-        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
-        if (r < 0)
-                return r;
-
-        LIST_FOREACH_SAFE(port, p, q, c->ports) {
-
-                /* Find the port */
-                if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
-                        lldp_neighbour_port_remove_and_free(p);
-                        break;
-                }
-        }
-
-        return 0;
-}
-
-int lldp_mib_add_objects(Prioq *by_expiry,
-                         Hashmap *neighbour_mib,
-                         tlv_packet *tlv) {
-        _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
-        _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
-        lldp_chassis_id chassis_id;
-        bool new_chassis = false;
-        uint8_t subtype, *data;
-        uint16_t ttl, length;
-        int r;
-
-        assert_return(by_expiry, -EINVAL);
-        assert_return(neighbour_mib, -EINVAL);
-        assert_return(tlv, -EINVAL);
-
-        r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
-        if (r < 0)
-                goto drop;
-
-        r = sd_lldp_packet_read_ttl(tlv, &ttl);
-        if (r < 0)
-                goto drop;
-
-        /* Make hash key */
-        chassis_id.type = subtype;
-        chassis_id.length = length;
-        chassis_id.data = data;
-
-        /* Try to find the Chassis */
-        c = hashmap_get(neighbour_mib, &chassis_id);
-        if (!c) {
-
-                /* Don't create chassis if ttl 0 is received . Silently drop it */
-                if (ttl == 0) {
-                        log_lldp("TTL value 0 received. Skiping Chassis creation.");
-                        goto drop;
-                }
-
-                /* Admission Control: Can we store this packet ? */
-                if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
-
-                        log_lldp("Exceeding number of chassie: %d. Dropping ...",
-                                 hashmap_size(neighbour_mib));
-                        goto drop;
-                }
-
-                r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
-                if (r < 0)
-                        goto drop;
-
-                new_chassis = true;
-
-                r = hashmap_put(neighbour_mib, &c->chassis_id, c);
-                if (r < 0)
-                        goto drop;
-
-        } else {
-
-                /* When the TTL field is set to zero, the receiving LLDP agent is notified all
-                 * system information associated with the LLDP agent/port is to be deleted */
-                if (ttl == 0) {
-                        log_lldp("TTL value 0 received . Deleting associated Port ...");
-
-                        lldp_mib_remove_objects(c, tlv);
-
-                        c = NULL;
-                        goto drop;
-                }
-
-                /* if we already have this port just update it */
-                r = lldp_mib_update_objects(c, tlv);
-                if (r >= 0) {
-                        c = NULL;
-                        return r;
-                }
-
-                /* Admission Control: Can this port attached to the existing chassis ? */
-                if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
-                        log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
-
-                        c = NULL;
-                        goto drop;
-                }
-        }
-
-        /* This is a new port */
-        r = lldp_neighbour_port_new(c, tlv, &p);
-        if (r < 0)
-                goto drop;
-
-        r = prioq_put(c->by_expiry, p, &p->prioq_idx);
-        if (r < 0)
-                goto drop;
-
-        /* Attach new port to chassis */
-        LIST_PREPEND(port, c->ports, p);
-        c->n_ref ++;
-
-        p = NULL;
-        c = NULL;
-
-        return 0;
-
- drop:
-        sd_lldp_packet_unref(tlv);
-
-        if (new_chassis)
-                hashmap_remove(neighbour_mib, &c->chassis_id);
-
-        return r;
-}
-
-void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
-        lldp_chassis *c;
-
-        assert(p);
-        assert(p->c);
-
-        c = p->c;
-
-        prioq_remove(c->by_expiry, p, &p->prioq_idx);
-
-        LIST_REMOVE(port, c->ports, p);
-        lldp_neighbour_port_free(p);
-
-        /* Drop the Chassis if no port is attached  */
-        c->n_ref --;
-        if (c->n_ref <= 1) {
-                hashmap_remove(c->neighbour_mib, &c->chassis_id);
-                lldp_chassis_free(c);
-        }
-}
-
-void lldp_neighbour_port_free(lldp_neighbour_port *p) {
-
-        if(!p)
-                return;
-
-        sd_lldp_packet_unref(p->packet);
-
-        free(p->data);
-        free(p);
-}
-
-int lldp_neighbour_port_new(lldp_chassis *c,
-                            tlv_packet *tlv,
-                            lldp_neighbour_port **ret) {
-        _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
-        uint16_t length, ttl;
-        uint8_t *data;
-        uint8_t type;
-        int r;
-
-        assert(tlv);
-
-        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
-        if (r < 0)
-                return r;
-
-        r = sd_lldp_packet_read_ttl(tlv, &ttl);
-        if (r < 0)
-                return r;
-
-        p = new0(lldp_neighbour_port, 1);
-        if (!p)
-                return -ENOMEM;
-
-        p->c = c;
-        p->type = type;
-        p->length = length;
-        p->packet = tlv;
-        p->prioq_idx = PRIOQ_IDX_NULL;
-        p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
-
-        p->data = memdup(data, length);
-        if (!p->data)
-                return -ENOMEM;
-
-        *ret = p;
-        p = NULL;
-
-        return 0;
-}
-
-void lldp_chassis_free(lldp_chassis *c) {
-
-        if (!c)
-                return;
-
-        if (c->n_ref > 1)
-                return;
-
-        free(c->chassis_id.data);
-        free(c);
-}
-
-int lldp_chassis_new(tlv_packet *tlv,
-                     Prioq *by_expiry,
-                     Hashmap *neighbour_mib,
-                     lldp_chassis **ret) {
-        _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
-        uint16_t length;
-        uint8_t *data;
-        uint8_t type;
-        int r;
-
-        assert(tlv);
-
-        r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
-        if (r < 0)
-                return r;
-
-        c = new0(lldp_chassis, 1);
-        if (!c)
-                return -ENOMEM;
-
-        c->n_ref = 1;
-        c->chassis_id.type = type;
-        c->chassis_id.length = length;
-
-        c->chassis_id.data = memdup(data, length);
-        if (!c->chassis_id.data)
-                return -ENOMEM;
-
-        LIST_HEAD_INIT(c->ports);
-
-        c->by_expiry = by_expiry;
-        c->neighbour_mib = neighbour_mib;
-
-        *ret = c;
-        c = NULL;
-
-        return 0;
-}
-
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        _cleanup_(sd_lldp_packet_unrefp) tlv_packet *packet = NULL;
-        tlv_packet *p;
-        uint16_t length;
-        int r;
-
-        assert(fd);
-        assert(userdata);
-
-        r = tlv_packet_new(&packet);
-        if (r < 0)
-                return r;
-
-        length = read(fd, &packet->pdu, sizeof(packet->pdu));
-
-        /* Silently drop the packet */
-        if ((size_t) length > ETHER_MAX_LEN)
-                return 0;
-
-        packet->userdata = userdata;
-
-        p = packet;
-        packet = NULL;
-
-        return lldp_handle_packet(p, (uint16_t) length);
-}
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h
index 15b4a11b..7592bc43 100644
--- a/src/systemd/src/libsystemd-network/lldp-internal.h
+++ b/src/systemd/src/libsystemd-network/lldp-internal.h
@@ -1,3 +1,5 @@
+#pragma once
+
 /***
   This file is part of systemd.
 
@@ -18,74 +20,34 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#pragma once
-
 #include "sd-event.h"
+#include "sd-lldp.h"
 
-#include "list.h"
-#include "lldp-tlv.h"
+#include "hashmap.h"
 #include "log.h"
 #include "prioq.h"
 
-typedef struct lldp_neighbour_port lldp_neighbour_port;
-typedef struct lldp_chassis lldp_chassis;
-typedef struct lldp_chassis_id lldp_chassis_id;
-typedef struct lldp_agent_statistics lldp_agent_statistics;
+struct sd_lldp {
+        int ifindex;
+        int fd;
 
-struct lldp_neighbour_port {
-        uint8_t type;
-        uint8_t *data;
+        sd_event *event;
+        int64_t event_priority;
+        sd_event_source *io_event_source;
+        sd_event_source *timer_event_source;
 
-        uint16_t length;
-        usec_t until;
+        Prioq *neighbor_by_expiry;
+        Hashmap *neighbor_by_id;
 
-        unsigned prioq_idx;
+        uint64_t neighbors_max;
 
-        lldp_chassis *c;
-        tlv_packet *packet;
+        sd_lldp_callback_t callback;
+        void *userdata;
 
-        LIST_FIELDS(lldp_neighbour_port, port);
-};
+        uint16_t capability_mask;
 
-int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret);
-void lldp_neighbour_port_free(lldp_neighbour_port *p);
-void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free);
-#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep)
-
-struct lldp_chassis_id {
-        uint8_t type;
-        uint16_t length;
-
-        uint8_t *data;
+        struct ether_addr filter_address;
 };
 
-struct lldp_chassis {
-        unsigned n_ref;
-
-        lldp_chassis_id chassis_id;
-
-        Prioq *by_expiry;
-        Hashmap *neighbour_mib;
-
-        LIST_HEAD(lldp_neighbour_port, ports);
-};
-
-int lldp_chassis_new(tlv_packet *tlv,
-                     Prioq *by_expiry,
-                     Hashmap *neighbour_mib,
-                     lldp_chassis **ret);
-
-void lldp_chassis_free(lldp_chassis *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free);
-#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep)
-
-int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
-int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
-int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
-
-int lldp_handle_packet(tlv_packet *m, uint16_t length);
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
-#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
+#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
+#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c
new file mode 100644
index 00000000..0515ce1e
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c
@@ -0,0 +1,797 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "ether-addr-util.h"
+#include "hexdecoct.h"
+#include "in-addr-util.h"
+#include "lldp-internal.h"
+#include "lldp-neighbor.h"
+#include "lldp.h"
+#include "unaligned.h"
+
+static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
+        const LLDPNeighborID *id = p;
+
+        siphash24_compress(id->chassis_id, id->chassis_id_size, state);
+        siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
+        siphash24_compress(id->port_id, id->port_id_size, state);
+        siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
+}
+
+static int lldp_neighbor_id_compare_func(const void *a, const void *b) {
+        const LLDPNeighborID *x = a, *y = b;
+        int r;
+
+        r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
+        if (r != 0)
+                return r;
+
+        if (x->chassis_id_size < y->chassis_id_size)
+                return -1;
+
+        if (x->chassis_id_size > y->chassis_id_size)
+                return 1;
+
+        r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
+        if (r != 0)
+                return r;
+
+        if (x->port_id_size < y->port_id_size)
+                return -1;
+        if (x->port_id_size > y->port_id_size)
+                return 1;
+
+        return 0;
+}
+
+const struct hash_ops lldp_neighbor_id_hash_ops = {
+        .hash = lldp_neighbor_id_hash_func,
+        .compare = lldp_neighbor_id_compare_func
+};
+
+int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
+        const sd_lldp_neighbor *x = a, *y = b;
+
+        if (x->until < y->until)
+                return -1;
+
+        if (x->until > y->until)
+                return 1;
+
+        return 0;
+}
+
+_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
+        if (!n)
+                return NULL;
+
+        assert(n->n_ref > 0 || n->lldp);
+        n->n_ref++;
+
+        return n;
+}
+
+static void lldp_neighbor_free(sd_lldp_neighbor *n) {
+        assert(n);
+
+        free(n->id.port_id);
+        free(n->id.chassis_id);
+        free(n->port_description);
+        free(n->system_name);
+        free(n->system_description);
+        free(n->chassis_id_as_string);
+        free(n->port_id_as_string);
+        free(n);
+}
+
+_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
+
+        /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
+         * the sd_lldp object. */
+
+        if (!n)
+                return NULL;
+
+        assert(n->n_ref > 0);
+        n->n_ref--;
+
+        if (n->n_ref <= 0 && !n->lldp)
+                lldp_neighbor_free(n);
+
+        return NULL;
+}
+
+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
+
+        /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
+
+        if (!n)
+                return NULL;
+
+        if (!n->lldp)
+                return NULL;
+
+        assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
+        assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
+
+        n->lldp = NULL;
+
+        if (n->n_ref <= 0)
+                lldp_neighbor_free(n);
+
+        return NULL;
+}
+
+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
+        sd_lldp_neighbor *n;
+
+        n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
+        if (!n)
+                return NULL;
+
+        n->raw_size = raw_size;
+        n->n_ref = 1;
+
+        return n;
+}
+
+static int parse_string(char **s, const void *q, size_t n) {
+        const char *p = q;
+        char *k;
+
+        assert(s);
+        assert(p || n == 0);
+
+        if (*s) {
+                log_lldp("Found duplicate string, ignoring field.");
+                return 0;
+        }
+
+        /* Strip trailing NULs, just to be nice */
+        while (n > 0 && p[n-1] == 0)
+                n--;
+
+        if (n <= 0) /* Ignore empty strings */
+                return 0;
+
+        /* Look for inner NULs */
+        if (memchr(p, 0, n)) {
+                log_lldp("Found inner NUL in string, ignoring field.");
+                return 0;
+        }
+
+        /* Let's escape weird chars, for security reasons */
+        k = cescape_length(p, n);
+        if (!k)
+                return -ENOMEM;
+
+        free(*s);
+        *s = k;
+
+        return 1;
+}
+
+int lldp_neighbor_parse(sd_lldp_neighbor *n) {
+        struct ether_header h;
+        const uint8_t *p;
+        size_t left;
+        int r;
+
+        assert(n);
+
+        if (n->raw_size < sizeof(struct ether_header)) {
+                log_lldp("Recieved truncated packet, ignoring.");
+                return -EBADMSG;
+        }
+
+        memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
+
+        if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
+                log_lldp("Received packet with wrong type, ignoring.");
+                return -EBADMSG;
+        }
+
+        if (h.ether_dhost[0] != 0x01 ||
+            h.ether_dhost[1] != 0x80 ||
+            h.ether_dhost[2] != 0xc2 ||
+            h.ether_dhost[3] != 0x00 ||
+            h.ether_dhost[4] != 0x00 ||
+            !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
+                log_lldp("Received packet with wrong destination address, ignoring.");
+                return -EBADMSG;
+        }
+
+        memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
+        memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
+
+        p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
+        left = n->raw_size - sizeof(struct ether_header);
+
+        for (;;) {
+                uint8_t type;
+                uint16_t length;
+
+                if (left < 2) {
+                        log_lldp("TLV lacks header, ignoring.");
+                        return -EBADMSG;
+                }
+
+                type = p[0] >> 1;
+                length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
+                p += 2, left -= 2;
+
+                if (left < length) {
+                        log_lldp("TLV truncated, ignoring datagram.");
+                        return -EBADMSG;
+                }
+
+                switch (type) {
+
+                case LLDP_TYPE_END:
+                        if (length != 0) {
+                                log_lldp("End marker TLV not zero-sized, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+                        if (left != 0) {
+                                log_lldp("Trailing garbage in datagram, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+
+                        goto end_marker;
+
+                case LLDP_TYPE_CHASSIS_ID:
+                        if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
+                                log_lldp("Chassis ID field size out of range, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+                        if (n->id.chassis_id) {
+                                log_lldp("Duplicate chassis ID field, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+
+                        n->id.chassis_id = memdup(p, length);
+                        if (!n->id.chassis_id)
+                                return -ENOMEM;
+
+                        n->id.chassis_id_size = length;
+                        break;
+
+                case LLDP_TYPE_PORT_ID:
+                        if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
+                                log_lldp("Port ID field size out of range, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+                        if (n->id.port_id) {
+                                log_lldp("Duplicate port ID field, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+
+                        n->id.port_id = memdup(p, length);
+                        if (!n->id.port_id)
+                                return -ENOMEM;
+
+                        n->id.port_id_size = length;
+                        break;
+
+                case LLDP_TYPE_TTL:
+                        if (length != 2) {
+                                log_lldp("TTL field has wrong size, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+
+                        if (n->has_ttl) {
+                                log_lldp("Duplicate TTL field, ignoring datagram.");
+                                return -EBADMSG;
+                        }
+
+                        n->ttl = unaligned_read_be16(p);
+                        n->has_ttl = true;
+                        break;
+
+                case LLDP_TYPE_PORT_DESCRIPTION:
+                        r = parse_string(&n->port_description, p, length);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case LLDP_TYPE_SYSTEM_NAME:
+                        r = parse_string(&n->system_name, p, length);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case LLDP_TYPE_SYSTEM_DESCRIPTION:
+                        r = parse_string(&n->system_description, p, length);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case LLDP_TYPE_SYSTEM_CAPABILITIES:
+                        if (length != 4)
+                                log_lldp("System capabilities field has wrong size, ignoring.");
+                        else {
+                                n->system_capabilities = unaligned_read_be16(p);
+                                n->enabled_capabilities = unaligned_read_be16(p + 2);
+                                n->has_capabilities = true;
+                        }
+
+                        break;
+
+                case LLDP_TYPE_PRIVATE:
+                        if (length < 4)
+                                log_lldp("Found private TLV that is too short, ignoring.");
+
+                        break;
+                }
+
+
+                p += length, left -= length;
+        }
+
+end_marker:
+        if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
+                log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
+                return -EBADMSG;
+
+        }
+
+        n->rindex = sizeof(struct ether_header);
+
+        return 0;
+}
+
+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
+        assert(n);
+
+        if (n->ttl > 0)
+                n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
+        else
+                n->until = 0;
+
+        if (n->lldp)
+                prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
+}
+
+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
+        if (a == b)
+                return true;
+
+        if (!a || !b)
+                return false;
+
+        if (a->raw_size != b->raw_size)
+                return false;
+
+        return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
+}
+
+_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+        assert_return(n, -EINVAL);
+        assert_return(address, -EINVAL);
+
+        *address = n->source_address;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+        assert_return(n, -EINVAL);
+        assert_return(address, -EINVAL);
+
+        *address = n->destination_address;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(size, -EINVAL);
+
+        *ret = LLDP_NEIGHBOR_RAW(n);
+        *size = n->raw_size;
+
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+        assert_return(n, -EINVAL);
+        assert_return(type, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(size, -EINVAL);
+
+        assert(n->id.chassis_id_size > 0);
+
+        *type = *(uint8_t*) n->id.chassis_id;
+        *ret = (uint8_t*) n->id.chassis_id + 1;
+        *size = n->id.chassis_id_size - 1;
+
+        return 0;
+}
+
+static int format_mac_address(const void *data, size_t sz, char **ret) {
+        struct ether_addr a;
+        char *k;
+
+        assert(data || sz <= 0);
+
+        if (sz != 7)
+                return 0;
+
+        memcpy(&a, (uint8_t*) data + 1, sizeof(a));
+
+        k = new(char, ETHER_ADDR_TO_STRING_MAX);
+        if (!k)
+                return -ENOMEM;
+
+        *ret = ether_addr_to_string(&a, k);
+        return 1;
+}
+
+static int format_network_address(const void *data, size_t sz, char **ret) {
+        union in_addr_union a;
+        int family, r;
+
+        if (sz == 6 && ((uint8_t*) data)[1] == 1) {
+                memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
+                family = AF_INET;
+        } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
+                memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
+                family = AF_INET6;
+        } else
+                return 0;
+
+        r = in_addr_to_string(family, &a, ret);
+        if (r < 0)
+                return r;
+        return 1;
+}
+
+_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+        char *k;
+        int r;
+
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (n->chassis_id_as_string) {
+                *ret = n->chassis_id_as_string;
+                return 0;
+        }
+
+        assert(n->id.chassis_id_size > 0);
+
+        switch (*(uint8_t*) n->id.chassis_id) {
+
+        case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
+        case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
+        case LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
+        case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
+        case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
+                k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
+                if (!k)
+                        return -ENOMEM;
+
+                goto done;
+
+        case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+                r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        goto done;
+
+                break;
+
+        case LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
+                r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        goto done;
+
+                break;
+        }
+
+        /* Generic fallback */
+        k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
+        if (!k)
+                return -ENOMEM;
+
+done:
+        *ret = n->chassis_id_as_string = k;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+        assert_return(n, -EINVAL);
+        assert_return(type, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(size, -EINVAL);
+
+        assert(n->id.port_id_size > 0);
+
+        *type = *(uint8_t*) n->id.port_id;
+        *ret = (uint8_t*) n->id.port_id + 1;
+        *size = n->id.port_id_size - 1;
+
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+        char *k;
+        int r;
+
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (n->port_id_as_string) {
+                *ret = n->port_id_as_string;
+                return 0;
+        }
+
+        assert(n->id.port_id_size > 0);
+
+        switch (*(uint8_t*) n->id.port_id) {
+
+        case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+        case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+        case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+        case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+                k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
+                if (!k)
+                        return -ENOMEM;
+
+                goto done;
+
+        case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+                r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        goto done;
+
+                break;
+
+        case LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
+                r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        goto done;
+
+                break;
+        }
+
+        /* Generic fallback */
+        k = hexmem(n->id.port_id, n->id.port_id_size);
+        if (!k)
+                return -ENOMEM;
+
+done:
+        *ret = n->port_id_as_string = k;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = n->ttl;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->system_name)
+                return -ENODATA;
+
+        *ret = n->system_name;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->system_description)
+                return -ENODATA;
+
+        *ret = n->system_description;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->port_description)
+                return -ENODATA;
+
+        *ret = n->port_description;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->has_capabilities)
+                return -ENODATA;
+
+        *ret = n->system_capabilities;
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->has_capabilities)
+                return -ENODATA;
+
+        *ret = n->enabled_capabilities;
+        return 0;
+}
+
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(raw || raw_size <= 0, -EINVAL);
+
+        n = lldp_neighbor_new(raw_size);
+        if (!n)
+                return -ENOMEM;
+
+        memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
+        r = lldp_neighbor_parse(n);
+        if (r < 0)
+                return r;
+
+        *ret = n;
+        n = 0;
+
+        return r;
+}
+
+_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
+        assert_return(n, -EINVAL);
+
+        assert(n->raw_size >= sizeof(struct ether_header));
+        n->rindex = sizeof(struct ether_header);
+
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
+        size_t length;
+
+        assert_return(n, -EINVAL);
+
+        if (n->rindex == n->raw_size) /* EOF */
+                return -ESPIPE;
+
+        if (n->rindex + 2 > n->raw_size) /* Truncated message */
+                return -EBADMSG;
+
+        length = LLDP_NEIGHBOR_LENGTH(n);
+        if (n->rindex + 2 + length > n->raw_size)
+                return -EBADMSG;
+
+        n->rindex += 2 + length;
+        return n->rindex < n->raw_size;
+}
+
+_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
+        assert_return(n, -EINVAL);
+        assert_return(type, -EINVAL);
+
+        if (n->rindex == n->raw_size) /* EOF */
+                return -ESPIPE;
+
+        if (n->rindex + 2 > n->raw_size)
+                return -EBADMSG;
+
+        *type = LLDP_NEIGHBOR_TYPE(n);
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
+        uint8_t k;
+        int r;
+
+        assert_return(n, -EINVAL);
+
+        r = sd_lldp_neighbor_tlv_get_type(n, &k);
+        if (r < 0)
+                return r;
+
+        return type == k;
+}
+
+_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) {
+        const uint8_t *d;
+        size_t length;
+        int r;
+
+        assert_return(n, -EINVAL);
+        assert_return(oui, -EINVAL);
+        assert_return(subtype, -EINVAL);
+
+        r = sd_lldp_neighbor_tlv_is_type(n, LLDP_TYPE_PRIVATE);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENXIO;
+
+        length = LLDP_NEIGHBOR_LENGTH(n);
+        if (length < 4)
+                return -EBADMSG;
+
+        if (n->rindex + 2 + length > n->raw_size)
+                return -EBADMSG;
+
+        d = LLDP_NEIGHBOR_DATA(n);
+        memcpy(oui, d, 3);
+        *subtype = d[3];
+
+        return 0;
+}
+
+_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) {
+        uint8_t k[3], st;
+        int r;
+
+        r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
+        if (r == -ENXIO)
+                return 0;
+        if (r < 0)
+                return r;
+
+        return memcmp(k, oui, 3) == 0 && st == subtype;
+}
+
+_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+        size_t length;
+
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(size, -EINVAL);
+
+        /* Note that this returns the full TLV, including the TLV header */
+
+        if (n->rindex + 2 > n->raw_size)
+                return -EBADMSG;
+
+        length = LLDP_NEIGHBOR_LENGTH(n);
+
+        if (n->rindex + 2 + length > n->raw_size)
+                return -EBADMSG;
+
+        *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
+        *size = length + 2;
+
+        return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h
new file mode 100644
index 00000000..f203bfa6
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h
@@ -0,0 +1,106 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2016 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "sd-lldp.h"
+
+#include "hash-funcs.h"
+#include "lldp-internal.h"
+#include "time-util.h"
+
+typedef struct LLDPNeighborID {
+        /* The spec calls this an "MSAP identifier" */
+        void *chassis_id;
+        size_t chassis_id_size;
+
+        void *port_id;
+        size_t port_id_size;
+} LLDPNeighborID;
+
+struct sd_lldp_neighbor {
+        /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
+        sd_lldp *lldp;
+        unsigned n_ref;
+
+        usec_t until;
+        unsigned prioq_idx;
+
+        struct ether_addr source_address;
+        struct ether_addr destination_address;
+
+        LLDPNeighborID id;
+
+        /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */
+        size_t raw_size;
+
+        /* The current read index for the iterative TLV interface */
+        size_t rindex;
+
+        /* And a couple of fields parsed out. */
+        bool has_ttl:1;
+        bool has_capabilities:1;
+        bool has_port_vlan_id:1;
+
+        uint16_t ttl;
+
+        uint16_t system_capabilities;
+        uint16_t enabled_capabilities;
+
+        char *port_description;
+        char *system_name;
+        char *system_description;
+
+        uint16_t port_vlan_id;
+
+        char *chassis_id_as_string;
+        char *port_id_as_string;
+};
+
+static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) {
+        return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor));
+}
+
+static inline uint8_t LLDP_NEIGHBOR_TYPE(const sd_lldp_neighbor *n) {
+        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1;
+}
+
+static inline size_t LLDP_NEIGHBOR_LENGTH(const sd_lldp_neighbor *n) {
+        uint8_t *p;
+
+        p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
+        return p[1] + (((size_t) (p[0] & 1)) << 8);
+}
+
+static inline void* LLDP_NEIGHBOR_DATA(const sd_lldp_neighbor *n) {
+        return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
+}
+
+extern const struct hash_ops lldp_neighbor_id_hash_ops;
+int lldp_neighbor_prioq_compare_func(const void *a, const void *b);
+
+sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n);
+sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
+int lldp_neighbor_parse(sd_lldp_neighbor *n);
+void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
+bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
index b96e9741..658448f6 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.c
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -21,65 +21,58 @@
 #include "nm-sd-adapt.h"
 
 #include <linux/filter.h>
-#include <linux/if_ether.h>
+#include <netinet/if_ether.h>
 
 #include "fd-util.h"
-#include "lldp-internal.h"
 #include "lldp-network.h"
-#include "lldp-tlv.h"
 #include "socket-util.h"
 
 int lldp_network_bind_raw_socket(int ifindex) {
-        typedef struct LLDPFrame {
-                struct ethhdr hdr;
-                uint8_t tlvs[0];
-        } LLDPFrame;
 
-        struct sock_filter filter[] = {
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)),      /* A <- 4 bytes of destination MAC */
+        static const struct sock_filter filter[] = {
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)),      /* A <- 4 bytes of destination MAC */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0),                    /* A != 01:80:c2:00 */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest) + 4),  /* A <- remaining 2 bytes of destination MAC */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4),  /* A <- remaining 2 bytes of destination MAC */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0),                        /* A != 00:00 */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0),                        /* A != 00:03 */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0),                        /* A != 00:0e */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_proto)),     /* A <- protocol */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)),     /* A <- protocol */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0),                /* A != ETHERTYPE_LLDP */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
                 BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1),                                 /* accept packet */
         };
 
-        struct sock_fprog fprog = {
+        static const struct sock_fprog fprog = {
                 .len = ELEMENTSOF(filter),
-                .filter = filter
+                .filter = (struct sock_filter*) filter,
         };
 
-        _cleanup_close_ int s = -1;
-
         union sockaddr_union saddrll = {
                 .ll.sll_family = AF_PACKET,
                 .ll.sll_ifindex = ifindex,
         };
 
+        _cleanup_close_ int fd = -1;
         int r;
 
         assert(ifindex > 0);
 
-        s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-        if (s < 0)
+        fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, htons(ETHERTYPE_LLDP));
+        if (fd < 0)
                 return -errno;
 
-        r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+        r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
         if (r < 0)
                 return -errno;
 
-        r = bind(s, &saddrll.sa, sizeof(saddrll.ll));
+        r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
         if (r < 0)
                 return -errno;
 
-        r = s;
-        s = -1;
+        r = fd;
+        fd = -1;
 
         return r;
 }
diff --git a/src/systemd/src/libsystemd-network/lldp-network.h b/src/systemd/src/libsystemd-network/lldp-network.h
index dcf31faa..c4cf8c79 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.h
+++ b/src/systemd/src/libsystemd-network/lldp-network.h
@@ -1,3 +1,5 @@
+#pragma once
+
 /***
   This file is part of systemd.
 
@@ -18,8 +20,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#pragma once
-
 #include "sd-event.h"
 
 int lldp_network_bind_raw_socket(int ifindex);
diff --git a/src/systemd/src/libsystemd-network/lldp-port.c b/src/systemd/src/libsystemd-network/lldp-port.c
deleted file mode 100644
index e96ef8a7..00000000
--- a/src/systemd/src/libsystemd-network/lldp-port.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 Tom Gundersen
-  Copyright (C) 2014 Susant Sahani
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "nm-sd-adapt.h"
-
-#include "alloc-util.h"
-#include "async.h"
-#include "lldp-internal.h"
-#include "lldp-network.h"
-#include "lldp-port.h"
-
-int lldp_port_start(lldp_port *p) {
-        int r;
-
-        assert_return(p, -EINVAL);
-
-        r = lldp_network_bind_raw_socket(p->ifindex);
-        if (r < 0)
-                return r;
-
-        p->rawfd = r;
-
-        r = sd_event_add_io(p->event, &p->lldp_port_rx,
-                            p->rawfd, EPOLLIN, lldp_receive_packet, p);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to allocate event source: %m");
-                goto fail;
-        }
-
-        r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to set event priority: %m");
-                goto fail;
-        }
-
-        r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
-        if (r < 0) {
-                log_debug_errno(r, "Failed to set event name: %m");
-                goto fail;
-        }
-
-        return 0;
-
-fail:
-        lldp_port_stop(p);
-
-        return r;
-}
-
-int lldp_port_stop(lldp_port *p) {
-
-        assert_return(p, -EINVAL);
-
-        p->rawfd = asynchronous_close(p->rawfd);
-        p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx);
-
-        return 0;
-}
-
-void lldp_port_free(lldp_port *p) {
-        if (!p)
-                return;
-
-        lldp_port_stop(p);
-
-        free(p->ifname);
-        free(p);
-}
-
-int lldp_port_new(int ifindex,
-                  const char *ifname,
-                  const struct ether_addr *addr,
-                  void *userdata,
-                  lldp_port **ret) {
-        _cleanup_free_ lldp_port *p = NULL;
-
-        assert_return(ifindex, -EINVAL);
-        assert_return(ifname, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        p = new0(lldp_port, 1);
-        if (!p)
-                return -ENOMEM;
-
-        p->rawfd = -1;
-        p->ifindex = ifindex;
-
-        p->ifname = strdup(ifname);
-        if (!p->ifname)
-                return -ENOMEM;
-
-        memcpy(&p->mac, addr, ETH_ALEN);
-
-        p->userdata = userdata;
-
-        *ret = p;
-
-        p = NULL;
-
-        return 0;
-}
diff --git a/src/systemd/src/libsystemd-network/lldp-port.h b/src/systemd/src/libsystemd-network/lldp-port.h
deleted file mode 100644
index 96092f8d..00000000
--- a/src/systemd/src/libsystemd-network/lldp-port.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 Tom Gundersen
-  Copyright (C) 2014 Susant Sahani
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <net/ethernet.h>
-
-#include "sd-event.h"
-#include "sd-lldp.h"
-
-#include "util.h"
-
-typedef struct lldp_port lldp_port;
-
-typedef enum LLDPPortStatus {
-        LLDP_PORT_STATUS_NONE,
-        LLDP_PORT_STATUS_ENABLED,
-        LLDP_PORT_STATUS_DISABLED,
-        _LLDP_PORT_STATUS_MAX,
-        _LLDP_PORT_STATUS_INVALID = -1,
-} LLDPPortStatus;
-
-struct lldp_port {
-        LLDPPortStatus status;
-
-        int ifindex;
-        char *ifname;
-
-        struct ether_addr mac;
-
-        int rawfd;
-
-        sd_event *event;
-        sd_event_source *lldp_port_rx;
-
-        int event_priority;
-
-        void *userdata;
-};
-
-int lldp_port_new(int ifindex,
-                  const char *ifname,
-                  const struct ether_addr *addr,
-                  void *userdata,
-                  lldp_port **ret);
-void lldp_port_free(lldp_port *p);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free);
-#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep)
-
-int lldp_port_start(lldp_port *p);
-int lldp_port_stop(lldp_port *p);
diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.c b/src/systemd/src/libsystemd-network/lldp-tlv.c
deleted file mode 100644
index 28161bc1..00000000
--- a/src/systemd/src/libsystemd-network/lldp-tlv.c
+++ /dev/null
@@ -1,640 +0,0 @@
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 Tom Gundersen
-  Copyright (C) 2014 Susant Sahani
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "nm-sd-adapt.h"
-
-#include <arpa/inet.h>
-#include <net/ethernet.h>
-
-#include "alloc-util.h"
-#include "lldp-tlv.h"
-#include "macro.h"
-
-int tlv_section_new(tlv_section **ret) {
-        tlv_section *s;
-
-        s = new0(tlv_section, 1);
-        if (!s)
-                return -ENOMEM;
-
-        *ret = s;
-
-        return 0;
-}
-
-void tlv_section_free(tlv_section *m) {
-
-        if (!m)
-                return;
-
-        free(m);
-}
-
-int tlv_packet_new(tlv_packet **ret) {
-        tlv_packet *m;
-
-        m = new0(tlv_packet, 1);
-        if (!m)
-                return -ENOMEM;
-
-        LIST_HEAD_INIT(m->sections);
-        m->n_ref = 1;
-
-        *ret = m;
-
-        return 0;
-}
-
-tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
-
-        if (!m)
-                return NULL;
-
-        assert(m->n_ref > 0);
-        m->n_ref++;
-
-        return m;
-}
-
-tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
-        tlv_section *s, *n;
-
-        if (!m)
-                return NULL;
-
-        assert(m->n_ref > 0);
-        m->n_ref--;
-
-        if (m->n_ref > 0)
-                return m;
-
-        LIST_FOREACH_SAFE(section, s, n, m->sections)
-                tlv_section_free(s);
-
-        free(m);
-        return NULL;
-}
-
-int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
-        uint8_t *p;
-
-        assert_return(m, -EINVAL);
-        assert_return(data, -EINVAL);
-        assert_return(data_length, -EINVAL);
-
-        if (m->length + data_length > ETHER_MAX_LEN)
-                return -ENOMEM;
-
-        p = m->pdu + m->length;
-        memcpy(p, data, data_length);
-        m->length += data_length;
-
-        return 0;
-}
-
-int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
-
-        assert_return(m, -EINVAL);
-
-        return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
-}
-
-int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
-        uint16_t type;
-
-        assert_return(m, -EINVAL);
-
-        type = htons(data);
-
-        return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
-}
-
-int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
-        uint32_t type;
-
-        assert_return(m, -EINVAL);
-
-        type = htonl(data);
-
-        return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
-}
-
-int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
-
-        assert_return(m, -EINVAL);
-
-        return tlv_packet_append_bytes(m, data, size);
-}
-
-int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
-
-        assert_return(m, -EINVAL);
-
-        m->container_pos = m->pdu + m->length;
-
-        return tlv_packet_append_u16(m, type << 9);
-}
-
-int lldp_tlv_packet_close_container(tlv_packet *m) {
-        uint16_t type;
-
-        assert_return(m, -EINVAL);
-        assert_return(m->container_pos, -EINVAL);
-
-        memcpy(&type, m->container_pos, sizeof(uint16_t));
-
-        type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
-        memcpy(m->container_pos, &type, sizeof(uint16_t));
-
-        return 0;
-}
-
-static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
-
-        assert_return(m->read_pos, -EINVAL);
-
-        *data = m->read_pos;
-
-        return 0;
-}
-
-int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
-        void *val = NULL;
-        int r;
-
-        assert_return(m, -EINVAL);
-
-        r = tlv_packet_read_internal(m->container,  &val);
-        if (r < 0)
-                return r;
-
-        memcpy(data, val, sizeof(uint8_t));
-
-        m->container->read_pos ++;
-
-        return 0;
-}
-
-int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
-        uint16_t t;
-        void *val = NULL;
-        int r;
-
-        assert_return(m, -EINVAL);
-
-        r = tlv_packet_read_internal(m->container, &val);
-        if (r < 0)
-                return r;
-
-        memcpy(&t, val, sizeof(uint16_t));
-        *data = ntohs(t);
-
-        m->container->read_pos += 2;
-
-        return 0;
-}
-
-int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
-        uint32_t t;
-        void *val;
-        int r;
-
-        assert_return(m, -EINVAL);
-
-        r = tlv_packet_read_internal(m->container, &val);
-        if (r < 0)
-                return r;
-
-        memcpy(&t, val, sizeof(uint32_t));
-        *data = ntohl(t);
-
-        m->container->read_pos += 4;
-
-        return r;
-}
-
-int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
-        void *val = NULL;
-        int r;
-
-        assert_return(m, -EINVAL);
-
-        r = tlv_packet_read_internal(m->container, &val);
-        if (r < 0)
-                return r;
-
-        *data = (char *) val;
-        *data_length = m->container->data + m->container->length - m->container->read_pos;
-
-        m->container->read_pos += *data_length;
-
-        return 0;
-}
-
-int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
-        void *val = NULL;
-        int r;
-
-        assert_return(m, -EINVAL);
-
-        r = tlv_packet_read_internal(m->container, &val);
-        if (r < 0)
-                return r;
-
-        *data = (uint8_t *) val;
-        *data_length = m->container->data + m->container->length - m->container->read_pos;
-
-        m->container->read_pos += *data_length;
-
-        return 0;
-}
-
-/* parse raw TLV packet */
-int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
-        tlv_section *section, *tail;
-        uint16_t t, l;
-        uint8_t *p;
-        int r;
-
-        assert_return(m, -EINVAL);
-        assert_return(size, -EINVAL);
-
-        p = m->pdu;
-
-        /* extract Ethernet header */
-        memcpy(&m->mac, p, ETH_ALEN);
-        p += sizeof(struct ether_header);
-
-        for (l = 0; l <= size; ) {
-                r = tlv_section_new(&section);
-                if (r < 0)
-                        return r;
-
-                memcpy(&t, p, sizeof(uint16_t));
-
-                section->type = ntohs(t) >> 9;
-                section->length = ntohs(t) & 0x01ff;
-
-                if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
-                        tlv_section_free(section);
-                        break;
-                }
-
-                p += 2;
-
-                if (section->type == LLDP_TYPE_PRIVATE &&
-                    section->length >= LLDP_OUI_LEN + 1) {
-                        section->oui = p;
-                        p += LLDP_OUI_LEN;
-                        section->subtype = *p++;
-
-                        section->length -= LLDP_OUI_LEN + 1;
-                        l += LLDP_OUI_LEN + 1;
-                }
-
-                section->data = p;
-
-                LIST_FIND_TAIL(section, m->sections, tail);
-                LIST_INSERT_AFTER(section, m->sections, tail, section);
-
-                p += section->length;
-                l += (section->length + 2);
-        }
-
-        return 0;
-}
-
-int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
-        tlv_section *s;
-
-        assert_return(m, -EINVAL);
-        assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
-
-        LIST_FOREACH(section, s, m->sections)
-                if (s->type == type)
-                        break;
-        if (!s)
-                return -1;
-
-        m->container = s;
-
-        m->container->read_pos = s->data;
-        if (!m->container->read_pos) {
-                m->container = NULL;
-                return -1;
-        }
-
-        return 0;
-}
-
-int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
-        tlv_section *s;
-
-        assert_return(m, -EINVAL);
-        assert_return(oui, -EINVAL);
-
-        LIST_FOREACH(section, s, m->sections) {
-                if (s->type == LLDP_TYPE_PRIVATE &&
-                    s->oui &&
-                    s->subtype == subtype &&
-                    !memcmp(s->oui, oui, LLDP_OUI_LEN))
-                        break;
-        }
-
-        if (!s)
-                return -1;
-
-        m->container = s;
-
-        m->container->read_pos = s->data;
-        if (!m->container->read_pos) {
-                m->container = NULL;
-                return -1;
-        }
-
-        return 0;
-}
-
-int lldp_tlv_packet_exit_container(tlv_packet *m) {
-        assert_return(m, -EINVAL);
-
-        m->container = 0;
-
-        return 0;
-}
-
-static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, type);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u16(tlv, value);
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
-        char *s;
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, type);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_string(tlv, &s, length);
-        if (r < 0)
-                goto out;
-
-        *data = (char *) s;
-
- out:
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
-                                   uint8_t *type,
-                                   uint8_t **data,
-                                   uint16_t *length) {
-        uint8_t subtype;
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u8(tlv, &subtype);
-        if (r < 0)
-                goto out;
-
-        switch (subtype) {
-        case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-
-                r = tlv_packet_read_bytes(tlv, data, length);
-                if (r < 0)
-                        goto out;
-
-                break;
-        default:
-                r = -EOPNOTSUPP;
-                break;
-        }
-
-        *type = subtype;
-
- out:
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_port_id(tlv_packet *tlv,
-                                uint8_t *type,
-                                uint8_t **data,
-                                uint16_t *length) {
-        uint8_t subtype;
-        char *s;
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u8(tlv, &subtype);
-        if (r < 0)
-                goto out;
-
-        switch (subtype) {
-        case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
-        case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
-        case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
-        case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-
-                r = tlv_packet_read_string(tlv, &s, length);
-                if (r < 0)
-                        goto out;
-
-                *data = (uint8_t *) s;
-
-                break;
-        case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-
-                r = tlv_packet_read_bytes(tlv, data, length);
-                if (r < 0)
-                        goto out;
-
-                break;
-        default:
-                r = -EOPNOTSUPP;
-                break;
-        }
-
-        *type = subtype;
-
- out:
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
-        return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
-}
-
-int sd_lldp_packet_read_system_name(tlv_packet *tlv,
-                                    char **data,
-                                    uint16_t *length) {
-        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
-}
-
-int sd_lldp_packet_read_system_description(tlv_packet *tlv,
-                                           char **data,
-                                           uint16_t *length) {
-        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
-}
-
-int sd_lldp_packet_read_port_description(tlv_packet *tlv,
-                                         char **data,
-                                         uint16_t *length) {
-        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
-}
-
-int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
-        return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
-}
-
-int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u16(tlv, id);
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u8(tlv, flags);
-        if (r >= 0)
-                r = tlv_packet_read_u16(tlv, id);
-
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
-        int r, r2;
-        uint8_t len = 0;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u16(tlv, vlan_id);
-        if (r >= 0)
-                r = tlv_packet_read_u8(tlv, &len);
-        if (r >= 0)
-                r = tlv_packet_read_string(tlv, name, length);
-
-        if (r >= 0 && len < *length)
-                *length = len;
-
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u16(tlv, id);
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
-        int r, r2;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u8(tlv, status);
-        if (r >= 0)
-                r = tlv_packet_read_u32(tlv, id);
-
-        r2 = lldp_tlv_packet_exit_container(tlv);
-
-        return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
-        assert_return(tlv, -EINVAL);
-        assert_return(dest, -EINVAL);
-
-        /* 802.1AB-2009, Table 7-1 */
-        if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
-                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
-        else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
-                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
-        else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
-                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
-        else
-                return -EINVAL;
-
-        return 0;
-}
diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.h b/src/systemd/src/libsystemd-network/lldp-tlv.h
deleted file mode 100644
index 8e7706c6..00000000
--- a/src/systemd/src/libsystemd-network/lldp-tlv.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/***
-  This file is part of systemd.
-
-  Copyright (C) 2014 Tom Gundersen
-  Copyright (C) 2014 Susant Sahani
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include <net/ethernet.h>
-
-#include "sd-lldp.h"
-
-#include "list.h"
-#include "lldp.h"
-#include "util.h"
-
-typedef struct sd_lldp_packet tlv_packet;
-typedef struct sd_lldp_section tlv_section;
-
-#define LLDP_OUI_LEN 3
-
-struct sd_lldp_section {
-        uint16_t type;
-        uint16_t length;
-        uint8_t *oui;
-        uint8_t subtype;
-
-        uint8_t *read_pos;
-        uint8_t *data;
-
-        LIST_FIELDS(tlv_section, section);
-};
-
-#define LLDP_MAC_NEAREST_BRIDGE          (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
-#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
-#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
-
-int tlv_section_new(tlv_section **ret);
-void tlv_section_free(tlv_section *ret);
-
-struct sd_lldp_packet {
-        unsigned n_ref;
-
-        uint16_t type;
-        uint16_t length;
-        usec_t ts;
-
-        uint8_t *container_pos;
-        uint8_t pdu[ETHER_MAX_LEN];
-
-        void *userdata;
-
-        struct ether_addr mac;
-        tlv_section *container;
-
-        LIST_HEAD(tlv_section, sections);
-};
-
-int tlv_packet_new(tlv_packet **ret);
-
-int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
-int lldp_tlv_packet_close_container(tlv_packet *m);
-
-int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length);
-int tlv_packet_append_u8(tlv_packet *m, uint8_t data);
-int tlv_packet_append_u16(tlv_packet *m, uint16_t data);
-int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
-int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
-
-int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
-int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
-int lldp_tlv_packet_exit_container(tlv_packet *m);
-
-int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
-int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length);
-int tlv_packet_read_u8(tlv_packet *m, uint8_t *data);
-int tlv_packet_read_u16(tlv_packet *m, uint16_t *data);
-int tlv_packet_read_u32(tlv_packet *m, uint32_t *data);
-
-int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size);
diff --git a/src/systemd/src/libsystemd-network/lldp.h b/src/systemd/src/libsystemd-network/lldp.h
index d2c71646..d61ecabc 100644
--- a/src/systemd/src/libsystemd-network/lldp.h
+++ b/src/systemd/src/libsystemd-network/lldp.h
@@ -1,3 +1,5 @@
+#pragma once
+
 /***
   This file is part of systemd.
 
@@ -18,14 +20,10 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#pragma once
-
 #define LLDP_MULTICAST_ADDR     { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
 
-#define ETHERTYPE_LLDP          0x88cc
-
 /* IEEE 802.3AB Clause 9: TLV Types */
-typedef enum LLDPTypes {
+enum {
         LLDP_TYPE_END                  =   0,
         LLDP_TYPE_CHASSIS_ID           =   1,
         LLDP_TYPE_PORT_ID              =   2,
@@ -36,12 +34,10 @@ typedef enum LLDPTypes {
         LLDP_TYPE_SYSTEM_CAPABILITIES  =   7,
         LLDP_TYPE_MGMT_ADDRESS         =   8,
         LLDP_TYPE_PRIVATE              =   127,
-        _LLDP_TYPE_MAX,
-        _LLDP_TYPE_INVALID             = -1,
-} LLDPTypes;
+};
 
 /* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
-typedef enum LLDPChassisSubtypes {
+enum {
         LLDP_CHASSIS_SUBTYPE_RESERVED            = 0,
         LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT   = 1,
         LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS     = 2,
@@ -50,25 +46,21 @@ typedef enum LLDPChassisSubtypes {
         LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS     = 5,
         LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME      = 6,
         LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED    = 7,
-        _LLDP_CHASSIS_SUBTYPE_MAX,
-        _LLDP_CHASSIS_SUBTYPE_INVALID            = -1,
-} LLDPChassisSubtypes;
+};
 
 /* IEEE 802.3AB Clause 9.5.3: Port subtype */
-typedef enum LLDPPortSubtypes  {
+enum {
         LLDP_PORT_SUBTYPE_RESERVED           = 0,
         LLDP_PORT_SUBTYPE_INTERFACE_ALIAS    = 1,
         LLDP_PORT_SUBTYPE_PORT_COMPONENT     = 2,
         LLDP_PORT_SUBTYPE_MAC_ADDRESS        = 3,
-        LLDP_PORT_SUBTYPE_NETWORK            = 4,
+        LLDP_PORT_SUBTYPE_NETWORK_ADDRESS    = 4,
         LLDP_PORT_SUBTYPE_INTERFACE_NAME     = 5,
         LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID   = 6,
         LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED   = 7,
-        _LLDP_PORT_SUBTYPE_MAX,
-        _LLDP_PORT_SUBTYPE_INVALID           = -1
-} LLDPPortSubtypes;
+};
 
-typedef enum LLDPSystemCapabilities {
+enum {
         LLDP_SYSTEM_CAPABILITIES_OTHER        = 1 << 0,
         LLDP_SYSTEM_CAPABILITIES_REPEATER     = 1 << 1,
         LLDP_SYSTEM_CAPABILITIES_BRIDGE       = 1 << 2,
@@ -80,47 +72,31 @@ typedef enum LLDPSystemCapabilities {
         LLDP_SYSTEM_CAPABILITIES_CVLAN        = 1 << 8,
         LLDP_SYSTEM_CAPABILITIES_SVLAN        = 1 << 9,
         LLDP_SYSTEM_CAPABILITIES_TPMR         = 1 << 10,
-        _LLDP_SYSTEM_CAPABILITIES_MAX,
-        _LLDP_SYSTEM_CAPABILITIES_INVALID     = -1,
-} LLDPSystemCapabilities;
+};
+
+#define _LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
 
-typedef enum LLDPMedSubtype {
-        LLDP_MED_SUBTYPE_RESERVED          = 0,
-        LLDP_MED_SUBTYPE_CAPABILITIES      = 1,
-        LLDP_MED_SUBTYPE_NETWORK_POLICY    = 2,
-        LLDP_MED_SUBTYPE_LOCATION_ID       = 3,
-        LLDP_MED_SUBTYPE_EXTENDED_PVMDI    = 4,
-        LLDP_MED_SUBTYPE_INV_HWREV         = 5,
-        LLDP_MED_SUBTYPE_INV_FWREV         = 6,
-        LLDP_MED_SUBTYPE_INV_SWREV         = 7,
-        LLDP_MED_SUBTYPE_INV_SERIAL        = 8,
-        LLDP_MED_SUBTYPE_INV_MANUFACTURER  = 9,
-        LLDP_MED_SUBTYPE_INV_MODELNAME     = 10,
-        LLDP_MED_SUBTYPE_INV_ASSETID       = 11,
-        _LLDP_MED_SUBTYPE_MAX,
-        _LLDP_MED_SUBTYPE_INVALID          = -1,
-} LLDPMedSubtype;
+#define _LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS                           \
+        ((uint16_t)                                                     \
+         (LLDP_SYSTEM_CAPABILITIES_REPEATER|                            \
+          LLDP_SYSTEM_CAPABILITIES_BRIDGE|                              \
+          LLDP_SYSTEM_CAPABILITIES_WLAN_AP|                             \
+          LLDP_SYSTEM_CAPABILITIES_ROUTER|                              \
+          LLDP_SYSTEM_CAPABILITIES_DOCSIS|                              \
+          LLDP_SYSTEM_CAPABILITIES_CVLAN|                               \
+          LLDP_SYSTEM_CAPABILITIES_SVLAN|                               \
+          LLDP_SYSTEM_CAPABILITIES_TPMR))
 
-typedef enum LLDPMedCapability {
-        LLDP_MED_CAPABILITY_CAPAPILITIES   = 1 << 0,
-        LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1,
-        LLDP_MED_CAPABILITY_LOCATION_ID    = 1 << 2,
-        LLDP_MED_CAPABILITY_EXTENDED_PSE   = 1 << 3,
-        LLDP_MED_CAPABILITY_EXTENDED_PD    = 1 << 4,
-        LLDP_MED_CAPABILITY_INVENTORY      = 1 << 5,
-        LLDP_MED_CAPABILITY_MAX,
-        LLDP_MED_CAPABILITY_INVALID        = -1,
-} LLDPMedCapability;
 
 #define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
 #define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
 
 enum {
-        LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID            = 1,
-        LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID   = 2,
-        LLDP_OUI_SUBTYPE_802_1_VLAN_NAME               = 3,
-        LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY       = 4,
-        LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST        = 5,
-        LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID          = 6,
-        LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION        = 7,
+        LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID            = 1,
+        LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID   = 2,
+        LLDP_OUI_802_1_SUBTYPE_VLAN_NAME               = 3,
+        LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY       = 4,
+        LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST        = 5,
+        LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID          = 6,
+        LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION        = 7,
 };
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 383660f8..4e61c4fa 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -381,7 +381,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
                 if (r <= 0)
                         continue;
 
-                size ++;
+                size++;
         }
 
         *ret = addresses;
@@ -488,7 +488,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
                         return -ENOMEM;
 
                 entry = strndup(word, len);
-                if(!entry)
+                if (!entry)
                         return -ENOMEM;
 
                 tok = entry;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index d00e1d34..40f3c8e9 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -103,7 +103,7 @@ struct sd_dhcp_client {
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
         sd_event_source *timeout_expire;
-        sd_dhcp_client_cb_t cb;
+        sd_dhcp_client_callback_t cb;
         void *userdata;
         sd_dhcp_lease *lease;
         usec_t start_delay;
@@ -123,7 +123,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
                                       uint32_t revents, void *userdata);
 static void client_stop(sd_dhcp_client *client, int error);
 
-int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
+int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_callback_t cb,
                                 void *userdata) {
         assert_return(client, -EINVAL);
 
@@ -960,7 +960,7 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
 
         if (client->start_delay) {
-                sd_event_now(client->event, clock_boottime_or_monotonic(), &usec);
+                assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
                 usec += client->start_delay;
         }
 
@@ -1527,20 +1527,17 @@ static int client_receive_message_udp(sd_event_source *s, int fd,
                                       uint32_t revents, void *userdata) {
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPMessage *message = NULL;
-        int buflen = 0, len, r;
         const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
         const struct ether_addr *expected_chaddr = NULL;
         uint8_t expected_hlen = 0;
+        ssize_t len, buflen;
 
         assert(s);
         assert(client);
 
-        r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0)
-                return -errno;
-        else if (buflen < 0)
-                /* this can't be right */
-                return -EIO;
+        buflen = next_datagram_size_fd(fd);
+        if (buflen < 0)
+                return buflen;
 
         message = malloc0(buflen);
         if (!message)
@@ -1618,17 +1615,15 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
         };
         struct cmsghdr *cmsg;
         bool checksum = true;
-        int buflen = 0, len, r;
+        ssize_t buflen, len;
+        int r;
 
         assert(s);
         assert(client);
 
-        r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0)
-                return -errno;
-        else if (buflen < 0)
-                /* this can't be right */
-                return -EIO;
+        buflen = next_datagram_size_fd(fd);
+        if (buflen < 0)
+                return buflen;
 
         packet = malloc0(buflen);
         if (!packet)
@@ -1698,8 +1693,7 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) {
         return 0;
 }
 
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
-                                int priority) {
+int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(client, -EINVAL);
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index 9665d327..ebec1f12 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -35,6 +35,7 @@
 #include "in-addr-util.h"
 #include "network-internal.h"
 #include "random-util.h"
+#include "socket-util.h"
 #include "string-table.h"
 #include "util.h"
 
@@ -65,7 +66,7 @@ struct sd_dhcp6_client {
         uint8_t retransmit_count;
         sd_event_source *timeout_resend;
         sd_event_source *timeout_resend_expire;
-        sd_dhcp6_client_cb_t cb;
+        sd_dhcp6_client_callback_t cb;
         void *userdata;
         struct duid duid;
         size_t duid_len;
@@ -112,7 +113,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
 
 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
 
-int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata) {
+int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_callback_t cb, void *userdata) {
         assert_return(client, -EINVAL);
 
         client->cb = cb;
@@ -893,18 +894,16 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
         sd_dhcp6_client *client = userdata;
         DHCP6_CLIENT_DONT_DESTROY(client);
         _cleanup_free_ DHCP6Message *message = NULL;
-        int r, buflen, len;
+        ssize_t buflen, len;
+        int r = 0;
 
         assert(s);
         assert(client);
         assert(client->event);
 
-        r = ioctl(fd, FIONREAD, &buflen);
-        if (r < 0)
-                return -errno;
-        else if (buflen < 0)
-                /* This really should not happen */
-                return -EIO;
+        buflen = next_datagram_size_fd(fd);
+        if (buflen < 0)
+                return buflen;
 
         message = malloc(buflen);
         if (!message)
@@ -1207,7 +1206,7 @@ error:
         return r;
 }
 
-int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority) {
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(client, -EINVAL);
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
index 99e663c1..9b5ce720 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -94,7 +94,7 @@ struct sd_ipv4acd {
         struct ether_addr mac_addr;
         sd_event *event;
         int event_priority;
-        sd_ipv4acd_cb_t cb;
+        sd_ipv4acd_callback_t cb;
         void* userdata;
 };
 
@@ -430,7 +430,7 @@ int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
         return 0;
 }
 
-int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(ll, -EINVAL);
@@ -449,7 +449,7 @@ int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
         return 0;
 }
 
-int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) {
         assert_return(ll, -EINVAL);
 
         ll->cb = cb;
@@ -458,7 +458,7 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata)
         return 0;
 }
 
-int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) {
         assert_return(ll, -EINVAL);
         assert_return(address, -EINVAL);
         assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index 119d00fc..391be15a 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -54,7 +54,7 @@ struct sd_ipv4ll {
 
         /* External */
         be32_t claimed_address;
-        sd_ipv4ll_cb_t cb;
+        sd_ipv4ll_callback_t cb;
         void* userdata;
 };
 
@@ -162,7 +162,7 @@ int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
         return sd_ipv4acd_detach_event(ll->acd);
 }
 
-int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
+int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(ll, -EINVAL);
@@ -174,7 +174,7 @@ int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
         return 0;
 }
 
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
+int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
         assert_return(ll, -EINVAL);
 
         ll->cb = cb;
@@ -183,7 +183,7 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
         return 0;
 }
 
-int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
+int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
         assert_return(ll, -EINVAL);
         assert_return(address, -EINVAL);
 
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
index 852d5358..71d15812 100644
--- a/src/systemd/src/libsystemd-network/sd-lldp.c
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -1,21 +1,21 @@
 /***
-    This file is part of systemd.
+  This file is part of systemd.
 
-    Copyright (C) 2014 Tom Gundersen
-    Copyright (C) 2014 Susant Sahani
+  Copyright (C) 2014 Tom Gundersen
+  Copyright (C) 2014 Susant Sahani
 
-    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 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.
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
 
-    You should have received a copy of the GNU Lesser General Public License
-    along with systemd; If not, see <http://www.gnu.org/licenses/>.
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include "nm-sd-adapt.h"
@@ -26,733 +26,473 @@
 
 #include "alloc-util.h"
 #include "fd-util.h"
-#include "fileio.h"
-#include "hashmap.h"
 #include "lldp-internal.h"
-#include "lldp-port.h"
-#include "lldp-tlv.h"
-#include "prioq.h"
-#include "siphash24.h"
-#include "string-util.h"
-
-typedef enum LLDPAgentRXState {
-        LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
-        LLDP_AGENT_RX_DELETE_AGED_INFO,
-        LLDP_AGENT_RX_LLDP_INITIALIZE,
-        LLDP_AGENT_RX_WAIT_FOR_FRAME,
-        LLDP_AGENT_RX_RX_FRAME,
-        LLDP_AGENT_RX_DELETE_INFO,
-        LLDP_AGENT_RX_UPDATE_INFO,
-        _LLDP_AGENT_RX_STATE_MAX,
-        _LLDP_AGENT_RX_INVALID = -1,
-} LLDPAgentRXState;
-
-/* Section 10.5.2.2 Reception counters */
-struct lldp_agent_statistics {
-        uint64_t stats_ageouts_total;
-        uint64_t stats_frames_discarded_total;
-        uint64_t stats_frames_in_errors_total;
-        uint64_t stats_frames_in_total;
-        uint64_t stats_tlvs_discarded_total;
-        uint64_t stats_tlvs_unrecognized_total;
-};
-
-struct sd_lldp {
-        lldp_port *port;
-
-        Prioq *by_expiry;
-        Hashmap *neighbour_mib;
-
-        sd_lldp_cb_t cb;
-
-        void *userdata;
-
-        LLDPAgentRXState rx_state;
-        lldp_agent_statistics statistics;
-};
-
-static void chassis_id_hash_func(const void *p, struct siphash *state) {
-        const lldp_chassis_id *id = p;
-
-        assert(id);
-        assert(id->data);
-
-        siphash24_compress(&id->length, sizeof(id->length), state);
-        siphash24_compress(id->data, id->length, state);
-}
+#include "lldp-neighbor.h"
+#include "lldp-network.h"
+#include "socket-util.h"
+#include "ether-addr-util.h"
 
-static int chassis_id_compare_func(const void *_a, const void *_b) {
-        const lldp_chassis_id *a, *b;
+#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
 
-        a = _a;
-        b = _b;
+static void lldp_flush_neighbors(sd_lldp *lldp) {
+        sd_lldp_neighbor *n;
 
-        assert(!a->length || a->data);
-        assert(!b->length || b->data);
+        assert(lldp);
 
-        if (a->type != b->type)
-                return -1;
+        while ((n = hashmap_first(lldp->neighbor_by_id)))
+                lldp_neighbor_unlink(n);
+}
 
-        if (a->length != b->length)
-                return a->length < b->length ? -1 : 1;
+static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
+        assert(lldp);
+        assert(n);
 
-        return memcmp(a->data, b->data, a->length);
-}
+        log_lldp("Invoking callback for '%c'.", event);
 
-static const struct hash_ops chassis_id_hash_ops = {
-        .hash = chassis_id_hash_func,
-        .compare = chassis_id_compare_func
-};
+        if (!lldp->callback)
+                return;
 
-static void lldp_mib_delete_objects(sd_lldp *lldp);
-static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state);
-static void lldp_run_state_machine(sd_lldp *ll);
+        lldp->callback(lldp, event, n, lldp->userdata);
+}
 
-static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
-        int r;
+static int lldp_make_space(sd_lldp *lldp, size_t extra) {
+        usec_t t = USEC_INFINITY;
+        bool changed = false;
 
         assert(lldp);
-        assert(tlv);
-
-        /* Remove expired packets */
-        if (prioq_size(lldp->by_expiry) > 0) {
 
-                lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO);
+        /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
+         * are free. */
 
-                lldp_mib_delete_objects(lldp);
-        }
+        for (;;) {
+                _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
 
-        r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
-        if (r < 0)
-                goto out;
+                n = prioq_peek(lldp->neighbor_by_expiry);
+                if (!n)
+                        break;
 
-        lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO);
+                sd_lldp_neighbor_ref(n);
 
-        log_lldp("Packet added. MIB size: %d , PQ size: %d",
-                 hashmap_size(lldp->neighbour_mib),
-                 prioq_size(lldp->by_expiry));
+                if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
+                        goto remove_one;
 
-        lldp->statistics.stats_frames_in_total ++;
+                if (t == USEC_INFINITY)
+                        t = now(clock_boottime_or_monotonic());
 
- out:
-        if (r < 0)
-                log_lldp("Receive frame failed: %s", strerror(-r));
+                if (n->until > t)
+                        break;
 
-        lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+        remove_one:
+                lldp_neighbor_unlink(n);
+                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
+                changed = true;
+        }
 
-        return 0;
+        return changed;
 }
 
-/* 10.3.2 LLDPDU validation: rxProcessFrame() */
-int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
-        bool system_description = false, system_name = false, chassis_id = false;
-        bool malformed = false, port_id = false, ttl = false, end = false;
-        uint16_t type, len, i, l, t;
-        lldp_port *port;
-        uint8_t *p, *q;
-        sd_lldp *lldp;
-        int r;
-
-        assert(tlv);
-        assert(length > 0);
-
-        port = (lldp_port *) tlv->userdata;
-        lldp = (sd_lldp *) port->userdata;
-
-        if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
-                log_lldp("Port: %s is disabled. Dropping.", lldp->port->ifname);
-                goto out;
-        }
+static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
+        assert(lldp);
+        assert(n);
 
-        lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME);
+        /* Don't keep data with a zero TTL */
+        if (n->ttl <= 0)
+                return false;
 
-        p = tlv->pdu;
-        p += sizeof(struct ether_header);
+        /* Filter out data from the filter address */
+        if (!ether_addr_is_null(&lldp->filter_address) &&
+            ether_addr_equal(&lldp->filter_address, &n->source_address))
+                return false;
 
-        for (i = 1, l = 0; l <= length; i++) {
+        /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
+         * no caps field set. */
+        if (n->has_capabilities &&
+            (n->enabled_capabilities & lldp->capability_mask) == 0)
+                return false;
 
-                memcpy(&t, p, sizeof(uint16_t));
+        /* Keep everything else */
+        return true;
+}
 
-                type = ntohs(t) >> 9;
-                len = ntohs(t) & 0x01ff;
+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
 
-                if (type == LLDP_TYPE_END) {
-                        if (len != 0) {
-                                log_lldp("TLV type end must be length 0 (not %d). Dropping.", len);
+static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
+        bool keep;
+        int r;
 
-                                malformed = true;
-                                goto out;
-                        }
+        assert(lldp);
+        assert(n);
+        assert(!n->lldp);
 
-                        end = true;
+        keep = lldp_keep_neighbor(lldp, n);
 
-                        break;
-                } else if (type >=_LLDP_TYPE_MAX) {
-                        log_lldp("TLV type: %d not recognized. Dropping.", type);
+        /* First retrieve the old entry for this MSAP */
+        old = hashmap_get(lldp->neighbor_by_id, &n->id);
+        if (old) {
+                sd_lldp_neighbor_ref(old);
 
-                        malformed = true;
-                        goto out;
+                if (!keep) {
+                        lldp_neighbor_unlink(old);
+                        lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
+                        return 0;
                 }
 
-                /* skip type and length encoding */
-                p += 2;
-                q = p;
-
-                p += len;
-                l += (len + 2);
+                if (lldp_neighbor_equal(n, old)) {
+                        /* Is this equal, then restart the TTL counter, but don't do anyting else. */
+                        lldp_start_timer(lldp, old);
+                        lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
+                        return 0;
+                }
 
-                if (i <= 3) {
-                        if (i != type) {
-                                log_lldp("TLV missing or out of order. Dropping.");
+                /* Data changed, remove the old entry, and add a new one */
+                lldp_neighbor_unlink(old);
 
-                                malformed = true;
-                                goto out;
-                        }
-                }
+        } else if (!keep)
+                return 0;
 
-                switch(type) {
-                case LLDP_TYPE_CHASSIS_ID:
+        /* Then, make room for at least one new neighbor */
+        lldp_make_space(lldp, 1);
 
-                        if (len < 2) {
-                                log_lldp("Received malformed Chassis ID TLV length: %d. Dropping.", len);
+        r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
+        if (r < 0)
+                goto finish;
 
-                                malformed = true;
-                                goto out;
-                        }
+        r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
+        if (r < 0) {
+                assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
+                goto finish;
+        }
 
-                        if (chassis_id) {
-                                log_lldp("Duplicate Chassis ID TLV found. Dropping.");
+        n->lldp = lldp;
 
-                                malformed = true;
-                                goto out;
-                        }
+        lldp_start_timer(lldp, n);
+        lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
 
-                        /* Look what subtype it has */
-                        if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED || *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
-                                log_lldp("Unknown subtype: %d found in Chassis ID TLV. Dropping.", *q);
+        return 1;
 
-                                malformed = true;
-                                goto out;
+finish:
+        if (old)
+                lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
 
-                        }
+        return r;
+}
 
-                        chassis_id = true;
+static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
+        int r;
 
-                        break;
-                case LLDP_TYPE_PORT_ID:
+        assert(lldp);
+        assert(n);
 
-                        if (len < 2) {
-                                log_lldp("Received malformed Port ID TLV length: %d. Dropping.", len);
+        r = lldp_neighbor_parse(n);
+        if (r == -EBADMSG) /* Ignore bad messages */
+                return 0;
+        if (r < 0)
+                return r;
 
-                                malformed = true;
-                                goto out;
-                        }
+        r = lldp_add_neighbor(lldp, n);
+        if (r < 0) {
+                log_lldp_errno(r, "Failed to add datagram. Ignoring.");
+                return 0;
+        }
 
-                        if (port_id) {
-                                log_lldp("Duplicate Port ID TLV found. Dropping.");
+        log_lldp("Successfully processed LLDP datagram.");
+        return 0;
+}
 
-                                malformed = true;
-                                goto out;
-                        }
+static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
+        ssize_t space, length;
+        sd_lldp *lldp = userdata;
 
-                        /* Look what subtype it has */
-                        if (*q == LLDP_PORT_SUBTYPE_RESERVED || *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
-                                log_lldp("Unknown subtype: %d found in Port ID TLV. Dropping.", *q);
+        assert(fd >= 0);
+        assert(lldp);
 
-                                malformed = true;
-                                goto out;
+        space = next_datagram_size_fd(fd);
+        if (space < 0)
+                return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
 
-                        }
+        n = lldp_neighbor_new(space);
+        if (!n)
+                return -ENOMEM;
 
-                        port_id = true;
+        length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
+        if (length < 0)
+                return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
 
-                        break;
-                case LLDP_TYPE_TTL:
+        if ((size_t) length != n->raw_size) {
+                log_lldp("Packet size mismatch.");
+                return -EINVAL;
+        }
 
-                        if(len != 2) {
-                                log_lldp("Received invalid TTL TLV lenth: %d. Dropping.", len);
+        return lldp_handle_datagram(lldp, n);
+}
 
-                                malformed = true;
-                                goto out;
-                        }
+_public_ int sd_lldp_start(sd_lldp *lldp) {
+        int r;
 
-                        if (ttl) {
-                                log_lldp("Duplicate TTL TLV found. Dropping.");
+        assert_return(lldp, -EINVAL);
 
-                                malformed = true;
-                                goto out;
-                        }
+        if (lldp->fd >= 0)
+                return 0;
 
-                        ttl = true;
+        assert(!lldp->io_event_source);
 
-                        break;
-                case LLDP_TYPE_SYSTEM_NAME:
+        lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
+        if (lldp->fd < 0)
+                return lldp->fd;
 
-                        /* According to RFC 1035 the length of a FQDN is limited to 255 characters */
-                        if (len > 255) {
-                                log_lldp("Received invalid system name length: %d. Dropping.", len);
-                                malformed = true;
-                                goto out;
-                        }
+        if (lldp->event) {
+                r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
+                if (r < 0)
+                        goto fail;
 
-                        if (system_name) {
-                                log_lldp("Duplicate system name found. Dropping.");
-                                malformed = true;
-                                goto out;
-                        }
+                r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
+                if (r < 0)
+                        goto fail;
 
-                        system_name = true;
+                (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
+        }
 
-                        break;
-                case LLDP_TYPE_SYSTEM_DESCRIPTION:
-
-                        /* 0 <= n <= 255 octets */
-                        if (len > 255) {
-                                log_lldp("Received invalid system description length: %d. Dropping.", len);
-                                malformed = true;
-                                goto out;
-                        }
-
-                        if (system_description) {
-                                log_lldp("Duplicate system description found. Dropping.");
-                                malformed = true;
-                                goto out;
-                        }
-
-                        system_description = true;
-                        break;
-                default:
+        return 1;
 
-                        if (len == 0) {
-                                log_lldp("TLV type: %d length 0 received. Dropping.", type);
+fail:
+        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
+        lldp->fd = safe_close(lldp->fd);
 
-                                malformed = true;
-                                goto out;
-                        }
-                        break;
-                }
-        }
+        return r;
+}
 
-        if(!chassis_id || !port_id || !ttl || !end) {
-                log_lldp("One or more mandatory TLV missing. Dropping.");
+_public_ int sd_lldp_stop(sd_lldp *lldp) {
+        assert_return(lldp, -EINVAL);
 
-                malformed = true;
-                goto out;
+        if (lldp->fd < 0)
+                return 0;
 
-        }
+        lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
+        lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
+        lldp->fd = safe_close(lldp->fd);
 
-        r = tlv_packet_parse_pdu(tlv, length);
-        if (r < 0) {
-                log_lldp("Failed to parse the TLV. Dropping.");
+        lldp_flush_neighbors(lldp);
 
-                malformed = true;
-                goto out;
-        }
+        return 1;
+}
 
-        return lldp_receive_frame(lldp, tlv);
+_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
+        int r;
 
- out:
-        lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+        assert_return(lldp, -EINVAL);
+        assert_return(lldp->fd < 0, -EBUSY);
+        assert_return(!lldp->event, -EBUSY);
 
-        if (malformed) {
-                lldp->statistics.stats_frames_discarded_total ++;
-                lldp->statistics.stats_frames_in_errors_total ++;
+        if (event)
+                lldp->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&lldp->event);
+                if (r < 0)
+                        return r;
         }
 
-        sd_lldp_packet_unref(tlv);
+        lldp->event_priority = priority;
 
         return 0;
 }
 
-static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
-        const lldp_neighbour_port *p = a, *q = b;
+_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
 
-        if (p->until < q->until)
-                return -1;
-
-        if (p->until > q->until)
-                return 1;
+        assert_return(lldp, -EINVAL);
+        assert_return(lldp->fd < 0, -EBUSY);
 
+        lldp->event = sd_event_unref(lldp->event);
         return 0;
 }
 
-static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) {
-
-        assert(lldp);
-        assert(state < _LLDP_AGENT_RX_STATE_MAX);
+_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
+        assert_return(lldp, -EINVAL);
 
-        lldp->rx_state = state;
+        lldp->callback = cb;
+        lldp->userdata = userdata;
 
-        lldp_run_state_machine(lldp);
+        return 0;
 }
 
-static void lldp_run_state_machine(sd_lldp *lldp) {
-        if (!lldp->cb)
-                return;
-
-        switch (lldp->rx_state) {
-        case LLDP_AGENT_RX_UPDATE_INFO:
-                lldp->cb(lldp, SD_LLDP_EVENT_UPDATE_INFO, lldp->userdata);
-                break;
-        default:
-                break;
-        }
-}
+_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
 
-/* 10.5.5.2.1 mibDeleteObjects ()
- * The mibDeleteObjects () procedure deletes all information in the LLDP remote
- * systems MIB associated with the MSAP identifier if an LLDPDU is received with
- * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
+        if (!lldp)
+                return NULL;
 
-static void lldp_mib_delete_objects(sd_lldp *lldp) {
-        lldp_neighbour_port *p;
-        usec_t t = 0;
+        lldp_flush_neighbors(lldp);
 
-        /* Remove all entries that are past their TTL */
-        for (;;) {
+        hashmap_free(lldp->neighbor_by_id);
+        prioq_free(lldp->neighbor_by_expiry);
 
-                if (prioq_size(lldp->by_expiry) <= 0)
-                        break;
+        sd_event_source_unref(lldp->io_event_source);
+        sd_event_source_unref(lldp->timer_event_source);
+        sd_event_unref(lldp->event);
+        safe_close(lldp->fd);
 
-                p = prioq_peek(lldp->by_expiry);
-                if (!p)
-                        break;
+        free(lldp);
 
-                if (t <= 0)
-                        t = now(clock_boottime_or_monotonic());
+        return NULL;
+}
 
-                if (p->until > t)
-                        break;
+_public_ int sd_lldp_new(sd_lldp **ret, int ifindex) {
+        _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
+        int r;
 
-                lldp_neighbour_port_remove_and_free(p);
+        assert_return(ret, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
 
-                lldp->statistics.stats_ageouts_total ++;
-        }
-}
+        lldp = new0(sd_lldp, 1);
+        if (!lldp)
+                return -ENOMEM;
 
-static void lldp_mib_objects_flush(sd_lldp *lldp) {
-        lldp_neighbour_port *p, *q;
-        lldp_chassis *c;
+        lldp->fd = -1;
+        lldp->ifindex = ifindex;
+        lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX;
+        lldp->capability_mask = (uint16_t) -1;
 
-        assert(lldp);
-        assert(lldp->neighbour_mib);
-        assert(lldp->by_expiry);
+        lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops);
+        if (!lldp->neighbor_by_id)
+                return -ENOMEM;
 
-        /* Drop all packets */
-        while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
+        r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
+        if (r < 0)
+                return r;
 
-                LIST_FOREACH_SAFE(port, p, q, c->ports) {
-                        lldp_neighbour_port_remove_and_free(p);
-                }
-        }
+        *ret = lldp;
+        lldp = NULL;
 
-        assert(hashmap_size(lldp->neighbour_mib) == 0);
-        assert(prioq_size(lldp->by_expiry) == 0);
+        return 0;
 }
 
-int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
-        _cleanup_free_ char *temp_path = NULL;
-        _cleanup_fclose_ FILE *f = NULL;
-        uint8_t *mac, *port_id, type;
-        lldp_neighbour_port *p;
-        uint16_t data = 0, length = 0;
-        char buf[LINE_MAX];
-        lldp_chassis *c;
-        usec_t time;
-        Iterator i;
-        int r;
+static int neighbor_compare_func(const void *a, const void *b) {
+        const sd_lldp_neighbor * const*x = a, * const *y = b;
 
-        assert(lldp);
-        assert(lldp_file);
+        return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id);
+}
 
-        r = fopen_temporary(lldp_file, &f, &temp_path);
-        if (r < 0)
-                goto fail;
-
-        fchmod(fileno(f), 0644);
-
-        HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
-                LIST_FOREACH(port, p, c->ports) {
-                        _cleanup_free_ char *s = NULL;
-                        char *k, *t;
-
-                        r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length);
-                        if (r < 0)
-                                continue;
-
-                        sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
-                                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
-
-                        s = strdup(buf);
-                        if (!s) {
-                                r = -ENOMEM;
-                                goto fail;
-                        }
-
-                        r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length);
-                        if (r < 0)
-                                continue;
-
-                        if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
-                                k = strndup((char *) port_id, length -1);
-                                if (!k) {
-                                        r = -ENOMEM;
-                                        goto fail;
-                                }
-
-                                sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
-                                free(k);
-                        } else {
-                                mac = port_id;
-                                sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
-                                        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
-                        }
-
-                        k = strappend(s, buf);
-                        if (!k) {
-                                r = -ENOMEM;
-                                goto fail;
-                        }
-
-                        free(s);
-                        s = k;
-
-                        time = now(clock_boottime_or_monotonic());
-
-                        /* Don't write expired packets */
-                        if (time - p->until <= 0)
-                                continue;
-
-                        sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
-
-                        k = strappend(s, buf);
-                        if (!k) {
-                                r = -ENOMEM;
-                                goto fail;
-                        }
-
-                        free(s);
-                        s = k;
-
-                        r = sd_lldp_packet_read_system_name(p->packet, &k, &length);
-                        if (r < 0)
-                                k = strappend(s, "'_NAME=N/A' ");
-                        else {
-                                t = strndup(k, length);
-                                if (!t) {
-                                        r = -ENOMEM;
-                                        goto fail;
-                                }
-
-                                k = strjoin(s, "'_NAME=", t, "' ", NULL);
-                                free(t);
-                        }
-
-                        if (!k) {
-                                r = -ENOMEM;
-                                goto fail;
-                        }
-
-                        free(s);
-                        s = k;
-
-                        (void) sd_lldp_packet_read_system_capability(p->packet, &data);
-
-                        sprintf(buf, "'_CAP=%x'", data);
-
-                        k = strappend(s, buf);
-                        if (!k) {
-                                r = -ENOMEM;
-                                goto fail;
-                        }
-
-                        free(s);
-                        s = k;
-
-                        fprintf(f, "%s\n", s);
-                }
-        }
+static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_lldp *lldp = userdata;
+        int r, q;
 
-        r = fflush_and_check(f);
+        r = lldp_make_space(lldp, 0);
         if (r < 0)
-                goto fail;
+                return log_lldp_errno(r, "Failed to make space: %m");
 
-        if (rename(temp_path, lldp_file) < 0) {
-                r = -errno;
-                goto fail;
-        }
+        q = lldp_start_timer(lldp, NULL);
+        if (q < 0)
+                return log_lldp_errno(q, "Failed to restart timer: %m");
 
         return 0;
-
- fail:
-        if (temp_path)
-                (void) unlink(temp_path);
-
-        return log_error_errno(r, "Failed to save lldp data %s: %m", lldp_file);
 }
 
-int sd_lldp_start(sd_lldp *lldp) {
+static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
+        sd_lldp_neighbor *n;
         int r;
 
-        assert_return(lldp, -EINVAL);
-        assert_return(lldp->port, -EINVAL);
+        assert(lldp);
 
-        lldp->port->status = LLDP_PORT_STATUS_ENABLED;
+        if (neighbor)
+                lldp_neighbor_start_ttl(neighbor);
 
-        lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
+        n = prioq_peek(lldp->neighbor_by_expiry);
+        if (!n) {
 
-        r = lldp_port_start(lldp->port);
-        if (r < 0) {
-                log_lldp("Failed to start Port : %s , %s",
-                         lldp->port->ifname,
-                         strerror(-r));
+                if (lldp->timer_event_source)
+                        return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF);
 
-                lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
-
-                return r;
+                return 0;
         }
 
-        lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
-
-        return 0;
-}
-
-int sd_lldp_stop(sd_lldp *lldp) {
-        int r;
+        if (lldp->timer_event_source) {
+                r = sd_event_source_set_time(lldp->timer_event_source, n->until);
+                if (r < 0)
+                        return r;
 
-        assert_return(lldp, -EINVAL);
-        assert_return(lldp->port, -EINVAL);
+                return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT);
+        }
 
-        lldp->port->status = LLDP_PORT_STATUS_DISABLED;
+        if (!lldp->event)
+                return 0;
 
-        r = lldp_port_stop(lldp->port);
+        r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp);
         if (r < 0)
                 return r;
 
-        lldp_mib_objects_flush(lldp);
+        r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority);
+        if (r < 0)
+                return r;
 
+        (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer");
         return 0;
 }
 
-int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
-        int r;
+_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
+        sd_lldp_neighbor **l = NULL, *n;
+        Iterator i;
+        int k = 0, r;
 
         assert_return(lldp, -EINVAL);
-        assert_return(!lldp->port->event, -EBUSY);
+        assert_return(ret, -EINVAL);
 
-        if (event)
-                lldp->port->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&lldp->port->event);
-                if (r < 0)
-                        return r;
+        if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
+                *ret = NULL;
+                return 0;
         }
 
-        lldp->port->event_priority = priority;
+        l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
+        if (!l)
+                return -ENOMEM;
 
-        return 0;
-}
+        r = lldp_start_timer(lldp, NULL);
+        if (r < 0) {
+                free(l);
+                return r;
+        }
 
-int sd_lldp_detach_event(sd_lldp *lldp) {
+        HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
+                l[k++] = sd_lldp_neighbor_ref(n);
 
-        assert_return(lldp, -EINVAL);
+        assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
 
-        lldp->port->event = sd_event_unref(lldp->port->event);
+        /* Return things in a stable order */
+        qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func);
+        *ret = l;
 
-        return 0;
+        return k;
 }
 
-int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
+_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
         assert_return(lldp, -EINVAL);
+        assert_return(m <= 0, -EINVAL);
 
-        lldp->cb = cb;
-        lldp->userdata = userdata;
+        lldp->neighbors_max = m;
+        lldp_make_space(lldp, 0);
 
         return 0;
 }
 
-sd_lldp* sd_lldp_unref(sd_lldp *lldp) {
-
-        if (!lldp)
-                return NULL;
-
-        /* Drop all packets */
-        lldp_mib_objects_flush(lldp);
-
-        lldp_port_free(lldp->port);
-
-        hashmap_free(lldp->neighbour_mib);
-        prioq_free(lldp->by_expiry);
-
-        free(lldp);
-        return NULL;
-}
-
-int sd_lldp_new(int ifindex,
-                const char *ifname,
-                const struct ether_addr *mac,
-                sd_lldp **ret) {
-        _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
-        int r;
-
-        assert_return(ret, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(ifname, -EINVAL);
-        assert_return(mac, -EINVAL);
-
-        lldp = new0(sd_lldp, 1);
-        if (!lldp)
-                return -ENOMEM;
-
-        r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
-        if (r < 0)
-                return r;
-
-        lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
-        if (!lldp->neighbour_mib)
-                return -ENOMEM;
-
-        r = prioq_ensure_allocated(&lldp->by_expiry,
-                                   ttl_expiry_item_prioq_compare_func);
-        if (r < 0)
-                return r;
-
-        lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;
+_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
+        assert_return(lldp, -EINVAL);
+        assert_return(mask != 0, -EINVAL);
 
-        *ret = lldp;
-        lldp = NULL;
+        lldp->capability_mask = mask;
 
         return 0;
 }
 
-int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) {
-        lldp_neighbour_port *p;
-        lldp_chassis *c;
-        Iterator iter;
-        unsigned count = 0, i;
-
+_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
         assert_return(lldp, -EINVAL);
-        assert_return(tlvs, -EINVAL);
 
-        HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
-                LIST_FOREACH(port, p, c->ports)
-                        count++;
-        }
+        /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
+         * that our own can be filtered out here. */
 
-        if (!count) {
-                *tlvs = NULL;
+        if (!addr) {
+                zero(lldp->filter_address);
                 return 0;
         }
 
-        *tlvs = new(sd_lldp_packet *, count);
-        if (!*tlvs)
-                return -ENOMEM;
-
-        i = 0;
-        HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
-                LIST_FOREACH(port, p, c->ports)
-                        (*tlvs)[i++] = sd_lldp_packet_ref(p->packet);
-        }
-
-        return count;
+        lldp->filter_address = *addr;
+        return 0;
 }
diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c
new file mode 100644
index 00000000..31e8e7fa
--- /dev/null
+++ b/src/systemd/src/libsystemd/sd-event/sd-event.c
@@ -0,0 +1,2905 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/wait.h>
+
+#if 0 /* NM_IGNORED */
+#include "sd-daemon.h"
+#endif
+#include "sd-event.h"
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hashmap.h"
+#include "list.h"
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
+#endif
+#include "prioq.h"
+#if 0 /* NM_IGNORED */
+#include "process-util.h"
+#endif
+#include "set.h"
+#if 0 /* NM_IGNORED */
+#include "signal-util.h"
+#endif
+#include "string-table.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "util.h"
+
+#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
+
+typedef enum EventSourceType {
+        SOURCE_IO,
+        SOURCE_TIME_REALTIME,
+        SOURCE_TIME_BOOTTIME,
+        SOURCE_TIME_MONOTONIC,
+        SOURCE_TIME_REALTIME_ALARM,
+        SOURCE_TIME_BOOTTIME_ALARM,
+        SOURCE_SIGNAL,
+        SOURCE_CHILD,
+        SOURCE_DEFER,
+        SOURCE_POST,
+        SOURCE_EXIT,
+        SOURCE_WATCHDOG,
+        _SOURCE_EVENT_SOURCE_TYPE_MAX,
+        _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
+} EventSourceType;
+
+static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
+        [SOURCE_IO] = "io",
+        [SOURCE_TIME_REALTIME] = "realtime",
+        [SOURCE_TIME_BOOTTIME] = "bootime",
+        [SOURCE_TIME_MONOTONIC] = "monotonic",
+        [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm",
+        [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm",
+        [SOURCE_SIGNAL] = "signal",
+        [SOURCE_CHILD] = "child",
+        [SOURCE_DEFER] = "defer",
+        [SOURCE_POST] = "post",
+        [SOURCE_EXIT] = "exit",
+        [SOURCE_WATCHDOG] = "watchdog",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
+
+/* All objects we use in epoll events start with this value, so that
+ * we know how to dispatch it */
+typedef enum WakeupType {
+        WAKEUP_NONE,
+        WAKEUP_EVENT_SOURCE,
+        WAKEUP_CLOCK_DATA,
+        WAKEUP_SIGNAL_DATA,
+        _WAKEUP_TYPE_MAX,
+        _WAKEUP_TYPE_INVALID = -1,
+} WakeupType;
+
+#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM)
+
+struct sd_event_source {
+        WakeupType wakeup;
+
+        unsigned n_ref;
+
+        sd_event *event;
+        void *userdata;
+        sd_event_handler_t prepare;
+
+        char *description;
+
+        EventSourceType type:5;
+        int enabled:3;
+        bool pending:1;
+        bool dispatching:1;
+        bool floating:1;
+
+        int64_t priority;
+        unsigned pending_index;
+        unsigned prepare_index;
+        unsigned pending_iteration;
+        unsigned prepare_iteration;
+
+        LIST_FIELDS(sd_event_source, sources);
+
+        union {
+                struct {
+                        sd_event_io_handler_t callback;
+                        int fd;
+                        uint32_t events;
+                        uint32_t revents;
+                        bool registered:1;
+                } io;
+                struct {
+                        sd_event_time_handler_t callback;
+                        usec_t next, accuracy;
+                        unsigned earliest_index;
+                        unsigned latest_index;
+                } time;
+                struct {
+                        sd_event_signal_handler_t callback;
+                        struct signalfd_siginfo siginfo;
+                        int sig;
+                } signal;
+                struct {
+                        sd_event_child_handler_t callback;
+                        siginfo_t siginfo;
+                        pid_t pid;
+                        int options;
+                } child;
+                struct {
+                        sd_event_handler_t callback;
+                } defer;
+                struct {
+                        sd_event_handler_t callback;
+                } post;
+                struct {
+                        sd_event_handler_t callback;
+                        unsigned prioq_index;
+                } exit;
+        };
+};
+
+struct clock_data {
+        WakeupType wakeup;
+        int fd;
+
+        /* For all clocks we maintain two priority queues each, one
+         * ordered for the earliest times the events may be
+         * dispatched, and one ordered by the latest times they must
+         * have been dispatched. The range between the top entries in
+         * the two prioqs is the time window we can freely schedule
+         * wakeups in */
+
+        Prioq *earliest;
+        Prioq *latest;
+        usec_t next;
+
+        bool needs_rearm:1;
+};
+
+struct signal_data {
+        WakeupType wakeup;
+
+        /* For each priority we maintain one signal fd, so that we
+         * only have to dequeue a single event per priority at a
+         * time. */
+
+        int fd;
+        int64_t priority;
+        sigset_t sigset;
+        sd_event_source *current;
+};
+
+struct sd_event {
+        unsigned n_ref;
+
+        int epoll_fd;
+        int watchdog_fd;
+
+        Prioq *pending;
+        Prioq *prepare;
+
+        /* timerfd_create() only supports these five clocks so far. We
+         * can add support for more clocks when the kernel learns to
+         * deal with them, too. */
+        struct clock_data realtime;
+        struct clock_data boottime;
+        struct clock_data monotonic;
+        struct clock_data realtime_alarm;
+        struct clock_data boottime_alarm;
+
+        usec_t perturb;
+
+        sd_event_source **signal_sources; /* indexed by signal number */
+        Hashmap *signal_data; /* indexed by priority */
+
+        Hashmap *child_sources;
+        unsigned n_enabled_child_sources;
+
+        Set *post_sources;
+
+        Prioq *exit;
+
+        pid_t original_pid;
+
+        unsigned iteration;
+        dual_timestamp timestamp;
+        usec_t timestamp_boottime;
+        int state;
+
+        bool exit_requested:1;
+        bool need_process_child:1;
+        bool watchdog:1;
+        bool profile_delays:1;
+
+        int exit_code;
+
+        pid_t tid;
+        sd_event **default_event_ptr;
+
+        usec_t watchdog_last, watchdog_period;
+
+        unsigned n_sources;
+
+        LIST_HEAD(sd_event_source, sources);
+
+        usec_t last_run, last_log;
+        unsigned delays[sizeof(usec_t) * 8];
+};
+
+static void source_disconnect(sd_event_source *s);
+
+static int pending_prioq_compare(const void *a, const void *b) {
+        const sd_event_source *x = a, *y = b;
+
+        assert(x->pending);
+        assert(y->pending);
+
+        /* Enabled ones first */
+        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+                return -1;
+        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+                return 1;
+
+        /* Lower priority values first */
+        if (x->priority < y->priority)
+                return -1;
+        if (x->priority > y->priority)
+                return 1;
+
+        /* Older entries first */
+        if (x->pending_iteration < y->pending_iteration)
+                return -1;
+        if (x->pending_iteration > y->pending_iteration)
+                return 1;
+
+        return 0;
+}
+
+static int prepare_prioq_compare(const void *a, const void *b) {
+        const sd_event_source *x = a, *y = b;
+
+        assert(x->prepare);
+        assert(y->prepare);
+
+        /* Enabled ones first */
+        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+                return -1;
+        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+                return 1;
+
+        /* Move most recently prepared ones last, so that we can stop
+         * preparing as soon as we hit one that has already been
+         * prepared in the current iteration */
+        if (x->prepare_iteration < y->prepare_iteration)
+                return -1;
+        if (x->prepare_iteration > y->prepare_iteration)
+                return 1;
+
+        /* Lower priority values first */
+        if (x->priority < y->priority)
+                return -1;
+        if (x->priority > y->priority)
+                return 1;
+
+        return 0;
+}
+
+static int earliest_time_prioq_compare(const void *a, const void *b) {
+        const sd_event_source *x = a, *y = b;
+
+        assert(EVENT_SOURCE_IS_TIME(x->type));
+        assert(x->type == y->type);
+
+        /* Enabled ones first */
+        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+                return -1;
+        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+                return 1;
+
+        /* Move the pending ones to the end */
+        if (!x->pending && y->pending)
+                return -1;
+        if (x->pending && !y->pending)
+                return 1;
+
+        /* Order by time */
+        if (x->time.next < y->time.next)
+                return -1;
+        if (x->time.next > y->time.next)
+                return 1;
+
+        return 0;
+}
+
+static usec_t time_event_source_latest(const sd_event_source *s) {
+        return usec_add(s->time.next, s->time.accuracy);
+}
+
+static int latest_time_prioq_compare(const void *a, const void *b) {
+        const sd_event_source *x = a, *y = b;
+
+        assert(EVENT_SOURCE_IS_TIME(x->type));
+        assert(x->type == y->type);
+
+        /* Enabled ones first */
+        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+                return -1;
+        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+                return 1;
+
+        /* Move the pending ones to the end */
+        if (!x->pending && y->pending)
+                return -1;
+        if (x->pending && !y->pending)
+                return 1;
+
+        /* Order by time */
+        if (time_event_source_latest(x) < time_event_source_latest(y))
+                return -1;
+        if (time_event_source_latest(x) > time_event_source_latest(y))
+                return 1;
+
+        return 0;
+}
+
+static int exit_prioq_compare(const void *a, const void *b) {
+        const sd_event_source *x = a, *y = b;
+
+        assert(x->type == SOURCE_EXIT);
+        assert(y->type == SOURCE_EXIT);
+
+        /* Enabled ones first */
+        if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
+                return -1;
+        if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
+                return 1;
+
+        /* Lower priority values first */
+        if (x->priority < y->priority)
+                return -1;
+        if (x->priority > y->priority)
+                return 1;
+
+        return 0;
+}
+
+static void free_clock_data(struct clock_data *d) {
+        assert(d);
+        assert(d->wakeup == WAKEUP_CLOCK_DATA);
+
+        safe_close(d->fd);
+        prioq_free(d->earliest);
+        prioq_free(d->latest);
+}
+
+static void event_free(sd_event *e) {
+        sd_event_source *s;
+
+        assert(e);
+
+        while ((s = e->sources)) {
+                assert(s->floating);
+                source_disconnect(s);
+                sd_event_source_unref(s);
+        }
+
+        assert(e->n_sources == 0);
+
+        if (e->default_event_ptr)
+                *(e->default_event_ptr) = NULL;
+
+        safe_close(e->epoll_fd);
+        safe_close(e->watchdog_fd);
+
+        free_clock_data(&e->realtime);
+        free_clock_data(&e->boottime);
+        free_clock_data(&e->monotonic);
+        free_clock_data(&e->realtime_alarm);
+        free_clock_data(&e->boottime_alarm);
+
+        prioq_free(e->pending);
+        prioq_free(e->prepare);
+        prioq_free(e->exit);
+
+        free(e->signal_sources);
+        hashmap_free(e->signal_data);
+
+        hashmap_free(e->child_sources);
+        set_free(e->post_sources);
+        free(e);
+}
+
+_public_ int sd_event_new(sd_event** ret) {
+        sd_event *e;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        e = new0(sd_event, 1);
+        if (!e)
+                return -ENOMEM;
+
+        e->n_ref = 1;
+        e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1;
+        e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY;
+        e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA;
+        e->original_pid = getpid();
+        e->perturb = USEC_INFINITY;
+
+        r = prioq_ensure_allocated(&e->pending, pending_prioq_compare);
+        if (r < 0)
+                goto fail;
+
+        e->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+        if (e->epoll_fd < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
+                log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
+                e->profile_delays = true;
+        }
+
+        *ret = e;
+        return 0;
+
+fail:
+        event_free(e);
+        return r;
+}
+
+_public_ sd_event* sd_event_ref(sd_event *e) {
+
+        if (!e)
+                return NULL;
+
+        assert(e->n_ref >= 1);
+        e->n_ref++;
+
+        return e;
+}
+
+_public_ sd_event* sd_event_unref(sd_event *e) {
+
+        if (!e)
+                return NULL;
+
+        assert(e->n_ref >= 1);
+        e->n_ref--;
+
+        if (e->n_ref <= 0)
+                event_free(e);
+
+        return NULL;
+}
+
+static bool event_pid_changed(sd_event *e) {
+        assert(e);
+
+        /* We don't support people creating an event loop and keeping
+         * it around over a fork(). Let's complain. */
+
+        return e->original_pid != getpid();
+}
+
+static void source_io_unregister(sd_event_source *s) {
+        int r;
+
+        assert(s);
+        assert(s->type == SOURCE_IO);
+
+        if (event_pid_changed(s->event))
+                return;
+
+        if (!s->io.registered)
+                return;
+
+        r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
+        if (r < 0)
+                log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m",
+                                strna(s->description), event_source_type_to_string(s->type));
+
+        s->io.registered = false;
+}
+
+static int source_io_register(
+                sd_event_source *s,
+                int enabled,
+                uint32_t events) {
+
+        struct epoll_event ev = {};
+        int r;
+
+        assert(s);
+        assert(s->type == SOURCE_IO);
+        assert(enabled != SD_EVENT_OFF);
+
+        ev.events = events;
+        ev.data.ptr = s;
+
+        if (enabled == SD_EVENT_ONESHOT)
+                ev.events |= EPOLLONESHOT;
+
+        if (s->io.registered)
+                r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev);
+        else
+                r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev);
+        if (r < 0)
+                return -errno;
+
+        s->io.registered = true;
+
+        return 0;
+}
+
+static clockid_t event_source_type_to_clock(EventSourceType t) {
+
+        switch (t) {
+
+        case SOURCE_TIME_REALTIME:
+                return CLOCK_REALTIME;
+
+        case SOURCE_TIME_BOOTTIME:
+                return CLOCK_BOOTTIME;
+
+        case SOURCE_TIME_MONOTONIC:
+                return CLOCK_MONOTONIC;
+
+        case SOURCE_TIME_REALTIME_ALARM:
+                return CLOCK_REALTIME_ALARM;
+
+        case SOURCE_TIME_BOOTTIME_ALARM:
+                return CLOCK_BOOTTIME_ALARM;
+
+        default:
+                return (clockid_t) -1;
+        }
+}
+
+static EventSourceType clock_to_event_source_type(clockid_t clock) {
+
+        switch (clock) {
+
+        case CLOCK_REALTIME:
+                return SOURCE_TIME_REALTIME;
+
+        case CLOCK_BOOTTIME:
+                return SOURCE_TIME_BOOTTIME;
+
+        case CLOCK_MONOTONIC:
+                return SOURCE_TIME_MONOTONIC;
+
+        case CLOCK_REALTIME_ALARM:
+                return SOURCE_TIME_REALTIME_ALARM;
+
+        case CLOCK_BOOTTIME_ALARM:
+                return SOURCE_TIME_BOOTTIME_ALARM;
+
+        default:
+                return _SOURCE_EVENT_SOURCE_TYPE_INVALID;
+        }
+}
+
+static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) {
+        assert(e);
+
+        switch (t) {
+
+        case SOURCE_TIME_REALTIME:
+                return &e->realtime;
+
+        case SOURCE_TIME_BOOTTIME:
+                return &e->boottime;
+
+        case SOURCE_TIME_MONOTONIC:
+                return &e->monotonic;
+
+        case SOURCE_TIME_REALTIME_ALARM:
+                return &e->realtime_alarm;
+
+        case SOURCE_TIME_BOOTTIME_ALARM:
+                return &e->boottime_alarm;
+
+        default:
+                return NULL;
+        }
+}
+
+static int event_make_signal_data(
+                sd_event *e,
+                int sig,
+                struct signal_data **ret) {
+
+        struct epoll_event ev = {};
+        struct signal_data *d;
+        bool added = false;
+        sigset_t ss_copy;
+        int64_t priority;
+        int r;
+
+        assert(e);
+
+        if (event_pid_changed(e))
+                return -ECHILD;
+
+        if (e->signal_sources && e->signal_sources[sig])
+                priority = e->signal_sources[sig]->priority;
+        else
+                priority = 0;
+
+        d = hashmap_get(e->signal_data, &priority);
+        if (d) {
+                if (sigismember(&d->sigset, sig) > 0) {
+                        if (ret)
+                                *ret = d;
+                        return 0;
+                }
+        } else {
+                r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops);
+                if (r < 0)
+                        return r;
+
+                d = new0(struct signal_data, 1);
+                if (!d)
+                        return -ENOMEM;
+
+                d->wakeup = WAKEUP_SIGNAL_DATA;
+                d->fd  = -1;
+                d->priority = priority;
+
+                r = hashmap_put(e->signal_data, &d->priority, d);
+                if (r < 0) {
+                        free(d);
+                        return r;
+                }
+
+                added = true;
+        }
+
+        ss_copy = d->sigset;
+        assert_se(sigaddset(&ss_copy, sig) >= 0);
+
+        r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC);
+        if (r < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        d->sigset = ss_copy;
+
+        if (d->fd >= 0) {
+                if (ret)
+                        *ret = d;
+                return 0;
+        }
+
+        d->fd = r;
+
+        ev.events = EPOLLIN;
+        ev.data.ptr = d;
+
+        r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev);
+        if (r < 0)  {
+                r = -errno;
+                goto fail;
+        }
+
+        if (ret)
+                *ret = d;
+
+        return 0;
+
+fail:
+        if (added) {
+                d->fd = safe_close(d->fd);
+                hashmap_remove(e->signal_data, &d->priority);
+                free(d);
+        }
+
+        return r;
+}
+
+static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) {
+        assert(e);
+        assert(d);
+
+        /* Turns off the specified signal in the signal data
+         * object. If the signal mask of the object becomes empty that
+         * way removes it. */
+
+        if (sigismember(&d->sigset, sig) == 0)
+                return;
+
+        assert_se(sigdelset(&d->sigset, sig) >= 0);
+
+        if (sigisemptyset(&d->sigset)) {
+
+                /* If all the mask is all-zero we can get rid of the structure */
+                hashmap_remove(e->signal_data, &d->priority);
+                assert(!d->current);
+                safe_close(d->fd);
+                free(d);
+                return;
+        }
+
+        assert(d->fd >= 0);
+
+        if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0)
+                log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m");
+}
+
+static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) {
+        struct signal_data *d;
+        static const int64_t zero_priority = 0;
+
+        assert(e);
+
+        /* Rechecks if the specified signal is still something we are
+         * interested in. If not, we'll unmask it, and possibly drop
+         * the signalfd for it. */
+
+        if (sig == SIGCHLD &&
+            e->n_enabled_child_sources > 0)
+                return;
+
+        if (e->signal_sources &&
+            e->signal_sources[sig] &&
+            e->signal_sources[sig]->enabled != SD_EVENT_OFF)
+                return;
+
+        /*
+         * The specified signal might be enabled in three different queues:
+         *
+         * 1) the one that belongs to the priority passed (if it is non-NULL)
+         * 2) the one that belongs to the priority of the event source of the signal (if there is one)
+         * 3) the 0 priority (to cover the SIGCHLD case)
+         *
+         * Hence, let's remove it from all three here.
+         */
+
+        if (priority) {
+                d = hashmap_get(e->signal_data, priority);
+                if (d)
+                        event_unmask_signal_data(e, d, sig);
+        }
+
+        if (e->signal_sources && e->signal_sources[sig]) {
+                d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority);
+                if (d)
+                        event_unmask_signal_data(e, d, sig);
+        }
+
+        d = hashmap_get(e->signal_data, &zero_priority);
+        if (d)
+                event_unmask_signal_data(e, d, sig);
+}
+
+static void source_disconnect(sd_event_source *s) {
+        sd_event *event;
+
+        assert(s);
+
+        if (!s->event)
+                return;
+
+        assert(s->event->n_sources > 0);
+
+        switch (s->type) {
+
+        case SOURCE_IO:
+                if (s->io.fd >= 0)
+                        source_io_unregister(s);
+
+                break;
+
+        case SOURCE_TIME_REALTIME:
+        case SOURCE_TIME_BOOTTIME:
+        case SOURCE_TIME_MONOTONIC:
+        case SOURCE_TIME_REALTIME_ALARM:
+        case SOURCE_TIME_BOOTTIME_ALARM: {
+                struct clock_data *d;
+
+                d = event_get_clock_data(s->event, s->type);
+                assert(d);
+
+                prioq_remove(d->earliest, s, &s->time.earliest_index);
+                prioq_remove(d->latest, s, &s->time.latest_index);
+                d->needs_rearm = true;
+                break;
+        }
+
+        case SOURCE_SIGNAL:
+                if (s->signal.sig > 0) {
+
+                        if (s->event->signal_sources)
+                                s->event->signal_sources[s->signal.sig] = NULL;
+
+                        event_gc_signal_data(s->event, &s->priority, s->signal.sig);
+                }
+
+                break;
+
+        case SOURCE_CHILD:
+                if (s->child.pid > 0) {
+                        if (s->enabled != SD_EVENT_OFF) {
+                                assert(s->event->n_enabled_child_sources > 0);
+                                s->event->n_enabled_child_sources--;
+                        }
+
+                        (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
+                        event_gc_signal_data(s->event, &s->priority, SIGCHLD);
+                }
+
+                break;
+
+        case SOURCE_DEFER:
+                /* nothing */
+                break;
+
+        case SOURCE_POST:
+                set_remove(s->event->post_sources, s);
+                break;
+
+        case SOURCE_EXIT:
+                prioq_remove(s->event->exit, s, &s->exit.prioq_index);
+                break;
+
+        default:
+                assert_not_reached("Wut? I shouldn't exist.");
+        }
+
+        if (s->pending)
+                prioq_remove(s->event->pending, s, &s->pending_index);
+
+        if (s->prepare)
+                prioq_remove(s->event->prepare, s, &s->prepare_index);
+
+        event = s->event;
+
+        s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID;
+        s->event = NULL;
+        LIST_REMOVE(sources, event->sources, s);
+        event->n_sources--;
+
+        if (!s->floating)
+                sd_event_unref(event);
+}
+
+static void source_free(sd_event_source *s) {
+        assert(s);
+
+        source_disconnect(s);
+        free(s->description);
+        free(s);
+}
+
+static int source_set_pending(sd_event_source *s, bool b) {
+        int r;
+
+        assert(s);
+        assert(s->type != SOURCE_EXIT);
+
+        if (s->pending == b)
+                return 0;
+
+        s->pending = b;
+
+        if (b) {
+                s->pending_iteration = s->event->iteration;
+
+                r = prioq_put(s->event->pending, s, &s->pending_index);
+                if (r < 0) {
+                        s->pending = false;
+                        return r;
+                }
+        } else
+                assert_se(prioq_remove(s->event->pending, s, &s->pending_index));
+
+        if (EVENT_SOURCE_IS_TIME(s->type)) {
+                struct clock_data *d;
+
+                d = event_get_clock_data(s->event, s->type);
+                assert(d);
+
+                prioq_reshuffle(d->earliest, s, &s->time.earliest_index);
+                prioq_reshuffle(d->latest, s, &s->time.latest_index);
+                d->needs_rearm = true;
+        }
+
+        if (s->type == SOURCE_SIGNAL && !b) {
+                struct signal_data *d;
+
+                d = hashmap_get(s->event->signal_data, &s->priority);
+                if (d && d->current == s)
+                        d->current = NULL;
+        }
+
+        return 0;
+}
+
+static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) {
+        sd_event_source *s;
+
+        assert(e);
+
+        s = new0(sd_event_source, 1);
+        if (!s)
+                return NULL;
+
+        s->n_ref = 1;
+        s->event = e;
+        s->floating = floating;
+        s->type = type;
+        s->pending_index = s->prepare_index = PRIOQ_IDX_NULL;
+
+        if (!floating)
+                sd_event_ref(e);
+
+        LIST_PREPEND(sources, e->sources, s);
+        e->n_sources++;
+
+        return s;
+}
+
+_public_ int sd_event_add_io(
+                sd_event *e,
+                sd_event_source **ret,
+                int fd,
+                uint32_t events,
+                sd_event_io_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(fd >= 0, -EBADF);
+        assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        s = source_new(e, !ret, SOURCE_IO);
+        if (!s)
+                return -ENOMEM;
+
+        s->wakeup = WAKEUP_EVENT_SOURCE;
+        s->io.fd = fd;
+        s->io.events = events;
+        s->io.callback = callback;
+        s->userdata = userdata;
+        s->enabled = SD_EVENT_ON;
+
+        r = source_io_register(s, s->enabled, events);
+        if (r < 0) {
+                source_free(s);
+                return r;
+        }
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+
+static void initialize_perturb(sd_event *e) {
+        sd_id128_t bootid = {};
+
+        /* When we sleep for longer, we try to realign the wakeup to
+           the same time wihtin each minute/second/250ms, so that
+           events all across the system can be coalesced into a single
+           CPU wakeup. However, let's take some system-specific
+           randomness for this value, so that in a network of systems
+           with synced clocks timer events are distributed a
+           bit. Here, we calculate a perturbation usec offset from the
+           boot ID. */
+
+        if (_likely_(e->perturb != USEC_INFINITY))
+                return;
+
+        if (sd_id128_get_boot(&bootid) >= 0)
+                e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE;
+}
+
+static int event_setup_timer_fd(
+                sd_event *e,
+                struct clock_data *d,
+                clockid_t clock) {
+
+        struct epoll_event ev = {};
+        int r, fd;
+
+        assert(e);
+        assert(d);
+
+        if (_likely_(d->fd >= 0))
+                return 0;
+
+        fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        ev.events = EPOLLIN;
+        ev.data.ptr = d;
+
+        r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+        if (r < 0) {
+                safe_close(fd);
+                return -errno;
+        }
+
+        d->fd = fd;
+        return 0;
+}
+
+static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) {
+        assert(s);
+
+        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
+}
+
+_public_ int sd_event_add_time(
+                sd_event *e,
+                sd_event_source **ret,
+                clockid_t clock,
+                uint64_t usec,
+                uint64_t accuracy,
+                sd_event_time_handler_t callback,
+                void *userdata) {
+
+        EventSourceType type;
+        sd_event_source *s;
+        struct clock_data *d;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(accuracy != (uint64_t) -1, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        if (!callback)
+                callback = time_exit_callback;
+
+        type = clock_to_event_source_type(clock);
+        assert_return(type >= 0, -EOPNOTSUPP);
+
+        d = event_get_clock_data(e, type);
+        assert(d);
+
+        r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare);
+        if (r < 0)
+                return r;
+
+        r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare);
+        if (r < 0)
+                return r;
+
+        if (d->fd < 0) {
+                r = event_setup_timer_fd(e, d, clock);
+                if (r < 0)
+                        return r;
+        }
+
+        s = source_new(e, !ret, type);
+        if (!s)
+                return -ENOMEM;
+
+        s->time.next = usec;
+        s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy;
+        s->time.callback = callback;
+        s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL;
+        s->userdata = userdata;
+        s->enabled = SD_EVENT_ONESHOT;
+
+        d->needs_rearm = true;
+
+        r = prioq_put(d->earliest, s, &s->time.earliest_index);
+        if (r < 0)
+                goto fail;
+
+        r = prioq_put(d->latest, s, &s->time.latest_index);
+        if (r < 0)
+                goto fail;
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+
+fail:
+        source_free(s);
+        return r;
+}
+
+#if 0 /* NM_IGNORED */
+static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        assert(s);
+
+        return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
+}
+
+_public_ int sd_event_add_signal(
+                sd_event *e,
+                sd_event_source **ret,
+                int sig,
+                sd_event_signal_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s;
+        struct signal_data *d;
+        sigset_t ss;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(sig > 0, -EINVAL);
+        assert_return(sig < _NSIG, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        if (!callback)
+                callback = signal_exit_callback;
+
+        r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
+        if (r != 0)
+                return -r;
+
+        if (!sigismember(&ss, sig))
+                return -EBUSY;
+
+        if (!e->signal_sources) {
+                e->signal_sources = new0(sd_event_source*, _NSIG);
+                if (!e->signal_sources)
+                        return -ENOMEM;
+        } else if (e->signal_sources[sig])
+                return -EBUSY;
+
+        s = source_new(e, !ret, SOURCE_SIGNAL);
+        if (!s)
+                return -ENOMEM;
+
+        s->signal.sig = sig;
+        s->signal.callback = callback;
+        s->userdata = userdata;
+        s->enabled = SD_EVENT_ON;
+
+        e->signal_sources[sig] = s;
+
+        r = event_make_signal_data(e, sig, &d);
+        if (r < 0) {
+                source_free(s);
+                return r;
+        }
+
+        /* Use the signal name as description for the event source by default */
+        (void) sd_event_source_set_description(s, signal_to_string(sig));
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+#endif /* NM_IGNORED */
+
+_public_ int sd_event_add_child(
+                sd_event *e,
+                sd_event_source **ret,
+                pid_t pid,
+                int options,
+                sd_event_child_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(pid > 1, -EINVAL);
+        assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
+        assert_return(options != 0, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        r = hashmap_ensure_allocated(&e->child_sources, NULL);
+        if (r < 0)
+                return r;
+
+        if (hashmap_contains(e->child_sources, PID_TO_PTR(pid)))
+                return -EBUSY;
+
+        s = source_new(e, !ret, SOURCE_CHILD);
+        if (!s)
+                return -ENOMEM;
+
+        s->child.pid = pid;
+        s->child.options = options;
+        s->child.callback = callback;
+        s->userdata = userdata;
+        s->enabled = SD_EVENT_ONESHOT;
+
+        r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
+        if (r < 0) {
+                source_free(s);
+                return r;
+        }
+
+        e->n_enabled_child_sources++;
+
+        r = event_make_signal_data(e, SIGCHLD, NULL);
+        if (r < 0) {
+                e->n_enabled_child_sources--;
+                source_free(s);
+                return r;
+        }
+
+        e->need_process_child = true;
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+
+_public_ int sd_event_add_defer(
+                sd_event *e,
+                sd_event_source **ret,
+                sd_event_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        s = source_new(e, !ret, SOURCE_DEFER);
+        if (!s)
+                return -ENOMEM;
+
+        s->defer.callback = callback;
+        s->userdata = userdata;
+        s->enabled = SD_EVENT_ONESHOT;
+
+        r = source_set_pending(s, true);
+        if (r < 0) {
+                source_free(s);
+                return r;
+        }
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+
+_public_ int sd_event_add_post(
+                sd_event *e,
+                sd_event_source **ret,
+                sd_event_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        r = set_ensure_allocated(&e->post_sources, NULL);
+        if (r < 0)
+                return r;
+
+        s = source_new(e, !ret, SOURCE_POST);
+        if (!s)
+                return -ENOMEM;
+
+        s->post.callback = callback;
+        s->userdata = userdata;
+        s->enabled = SD_EVENT_ON;
+
+        r = set_put(e->post_sources, s);
+        if (r < 0) {
+                source_free(s);
+                return r;
+        }
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+
+_public_ int sd_event_add_exit(
+                sd_event *e,
+                sd_event_source **ret,
+                sd_event_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        r = prioq_ensure_allocated(&e->exit, exit_prioq_compare);
+        if (r < 0)
+                return r;
+
+        s = source_new(e, !ret, SOURCE_EXIT);
+        if (!s)
+                return -ENOMEM;
+
+        s->exit.callback = callback;
+        s->userdata = userdata;
+        s->exit.prioq_index = PRIOQ_IDX_NULL;
+        s->enabled = SD_EVENT_ONESHOT;
+
+        r = prioq_put(s->event->exit, s, &s->exit.prioq_index);
+        if (r < 0) {
+                source_free(s);
+                return r;
+        }
+
+        if (ret)
+                *ret = s;
+
+        return 0;
+}
+
+_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) {
+
+        if (!s)
+                return NULL;
+
+        assert(s->n_ref >= 1);
+        s->n_ref++;
+
+        return s;
+}
+
+_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) {
+
+        if (!s)
+                return NULL;
+
+        assert(s->n_ref >= 1);
+        s->n_ref--;
+
+        if (s->n_ref <= 0) {
+                /* Here's a special hack: when we are called from a
+                 * dispatch handler we won't free the event source
+                 * immediately, but we will detach the fd from the
+                 * epoll. This way it is safe for the caller to unref
+                 * the event source and immediately close the fd, but
+                 * we still retain a valid event source object after
+                 * the callback. */
+
+                if (s->dispatching) {
+                        if (s->type == SOURCE_IO)
+                                source_io_unregister(s);
+
+                        source_disconnect(s);
+                } else
+                        source_free(s);
+        }
+
+        return NULL;
+}
+
+_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) {
+        assert_return(s, -EINVAL);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        return free_and_strdup(&s->description, description);
+}
+
+_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
+        assert_return(s, -EINVAL);
+        assert_return(description, -EINVAL);
+        assert_return(s->description, -ENXIO);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *description = s->description;
+        return 0;
+}
+
+_public_ sd_event *sd_event_source_get_event(sd_event_source *s) {
+        assert_return(s, NULL);
+
+        return s->event;
+}
+
+_public_ int sd_event_source_get_pending(sd_event_source *s) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type != SOURCE_EXIT, -EDOM);
+        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        return s->pending;
+}
+
+_public_ int sd_event_source_get_io_fd(sd_event_source *s) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_IO, -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        return s->io.fd;
+}
+
+_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
+        int r;
+
+        assert_return(s, -EINVAL);
+        assert_return(fd >= 0, -EBADF);
+        assert_return(s->type == SOURCE_IO, -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        if (s->io.fd == fd)
+                return 0;
+
+        if (s->enabled == SD_EVENT_OFF) {
+                s->io.fd = fd;
+                s->io.registered = false;
+        } else {
+                int saved_fd;
+
+                saved_fd = s->io.fd;
+                assert(s->io.registered);
+
+                s->io.fd = fd;
+                s->io.registered = false;
+
+                r = source_io_register(s, s->enabled, s->io.events);
+                if (r < 0) {
+                        s->io.fd = saved_fd;
+                        s->io.registered = true;
+                        return r;
+                }
+
+                epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
+        }
+
+        return 0;
+}
+
+_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
+        assert_return(s, -EINVAL);
+        assert_return(events, -EINVAL);
+        assert_return(s->type == SOURCE_IO, -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *events = s->io.events;
+        return 0;
+}
+
+_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) {
+        int r;
+
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_IO, -EDOM);
+        assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
+        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        /* edge-triggered updates are never skipped, so we can reset edges */
+        if (s->io.events == events && !(events & EPOLLET))
+                return 0;
+
+        if (s->enabled != SD_EVENT_OFF) {
+                r = source_io_register(s, s->enabled, events);
+                if (r < 0)
+                        return r;
+        }
+
+        s->io.events = events;
+        source_set_pending(s, false);
+
+        return 0;
+}
+
+_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) {
+        assert_return(s, -EINVAL);
+        assert_return(revents, -EINVAL);
+        assert_return(s->type == SOURCE_IO, -EDOM);
+        assert_return(s->pending, -ENODATA);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *revents = s->io.revents;
+        return 0;
+}
+
+_public_ int sd_event_source_get_signal(sd_event_source *s) {
+        assert_return(s, -EINVAL);
+        assert_return(s->type == SOURCE_SIGNAL, -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        return s->signal.sig;
+}
+
+_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) {
+        assert_return(s, -EINVAL);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        return s->priority;
+}
+
+_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) {
+        int r;
+
+        assert_return(s, -EINVAL);
+        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        if (s->priority == priority)
+                return 0;
+
+        if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) {
+                struct signal_data *old, *d;
+
+                /* Move us from the signalfd belonging to the old
+                 * priority to the signalfd of the new priority */
+
+                assert_se(old = hashmap_get(s->event->signal_data, &s->priority));
+
+                s->priority = priority;
+
+                r = event_make_signal_data(s->event, s->signal.sig, &d);
+                if (r < 0) {
+                        s->priority = old->priority;
+                        return r;
+                }
+
+                event_unmask_signal_data(s->event, old, s->signal.sig);
+        } else
+                s->priority = priority;
+
+        if (s->pending)
+                prioq_reshuffle(s->event->pending, s, &s->pending_index);
+
+        if (s->prepare)
+                prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
+
+        if (s->type == SOURCE_EXIT)
+                prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
+
+        return 0;
+}
+
+_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) {
+        assert_return(s, -EINVAL);
+        assert_return(m, -EINVAL);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *m = s->enabled;
+        return 0;
+}
+
+_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
+        int r;
+
+        assert_return(s, -EINVAL);
+        assert_return(m == SD_EVENT_OFF || m == SD_EVENT_ON || m == SD_EVENT_ONESHOT, -EINVAL);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        /* If we are dead anyway, we are fine with turning off
+         * sources, but everything else needs to fail. */
+        if (s->event->state == SD_EVENT_FINISHED)
+                return m == SD_EVENT_OFF ? 0 : -ESTALE;
+
+        if (s->enabled == m)
+                return 0;
+
+        if (m == SD_EVENT_OFF) {
+
+                switch (s->type) {
+
+                case SOURCE_IO:
+                        source_io_unregister(s);
+                        s->enabled = m;
+                        break;
+
+                case SOURCE_TIME_REALTIME:
+                case SOURCE_TIME_BOOTTIME:
+                case SOURCE_TIME_MONOTONIC:
+                case SOURCE_TIME_REALTIME_ALARM:
+                case SOURCE_TIME_BOOTTIME_ALARM: {
+                        struct clock_data *d;
+
+                        s->enabled = m;
+                        d = event_get_clock_data(s->event, s->type);
+                        assert(d);
+
+                        prioq_reshuffle(d->earliest, s, &s->time.earliest_index);
+                        prioq_reshuffle(d->latest, s, &s->time.latest_index);
+                        d->needs_rearm = true;
+                        break;
+                }
+
+                case SOURCE_SIGNAL:
+                        s->enabled = m;
+
+                        event_gc_signal_data(s->event, &s->priority, s->signal.sig);
+                        break;
+
+                case SOURCE_CHILD:
+                        s->enabled = m;
+
+                        assert(s->event->n_enabled_child_sources > 0);
+                        s->event->n_enabled_child_sources--;
+
+                        event_gc_signal_data(s->event, &s->priority, SIGCHLD);
+                        break;
+
+                case SOURCE_EXIT:
+                        s->enabled = m;
+                        prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
+                        break;
+
+                case SOURCE_DEFER:
+                case SOURCE_POST:
+                        s->enabled = m;
+                        break;
+
+                default:
+                        assert_not_reached("Wut? I shouldn't exist.");
+                }
+
+        } else {
+                switch (s->type) {
+
+                case SOURCE_IO:
+                        r = source_io_register(s, m, s->io.events);
+                        if (r < 0)
+                                return r;
+
+                        s->enabled = m;
+                        break;
+
+                case SOURCE_TIME_REALTIME:
+                case SOURCE_TIME_BOOTTIME:
+                case SOURCE_TIME_MONOTONIC:
+                case SOURCE_TIME_REALTIME_ALARM:
+                case SOURCE_TIME_BOOTTIME_ALARM: {
+                        struct clock_data *d;
+
+                        s->enabled = m;
+                        d = event_get_clock_data(s->event, s->type);
+                        assert(d);
+
+                        prioq_reshuffle(d->earliest, s, &s->time.earliest_index);
+                        prioq_reshuffle(d->latest, s, &s->time.latest_index);
+                        d->needs_rearm = true;
+                        break;
+                }
+
+                case SOURCE_SIGNAL:
+
+                        s->enabled = m;
+
+                        r = event_make_signal_data(s->event, s->signal.sig, NULL);
+                        if (r < 0) {
+                                s->enabled = SD_EVENT_OFF;
+                                event_gc_signal_data(s->event, &s->priority, s->signal.sig);
+                                return r;
+                        }
+
+                        break;
+
+                case SOURCE_CHILD:
+
+                        if (s->enabled == SD_EVENT_OFF)
+                                s->event->n_enabled_child_sources++;
+
+                        s->enabled = m;
+
+                        r = event_make_signal_data(s->event, SIGCHLD, NULL);
+                        if (r < 0) {
+                                s->enabled = SD_EVENT_OFF;
+                                s->event->n_enabled_child_sources--;
+                                event_gc_signal_data(s->event, &s->priority, SIGCHLD);
+                                return r;
+                        }
+
+                        break;
+
+                case SOURCE_EXIT:
+                        s->enabled = m;
+                        prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index);
+                        break;
+
+                case SOURCE_DEFER:
+                case SOURCE_POST:
+                        s->enabled = m;
+                        break;
+
+                default:
+                        assert_not_reached("Wut? I shouldn't exist.");
+                }
+        }
+
+        if (s->pending)
+                prioq_reshuffle(s->event->pending, s, &s->pending_index);
+
+        if (s->prepare)
+                prioq_reshuffle(s->event->prepare, s, &s->prepare_index);
+
+        return 0;
+}
+
+_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) {
+        assert_return(s, -EINVAL);
+        assert_return(usec, -EINVAL);
+        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *usec = s->time.next;
+        return 0;
+}
+
+_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) {
+        struct clock_data *d;
+
+        assert_return(s, -EINVAL);
+        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
+        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        s->time.next = usec;
+
+        source_set_pending(s, false);
+
+        d = event_get_clock_data(s->event, s->type);
+        assert(d);
+
+        prioq_reshuffle(d->earliest, s, &s->time.earliest_index);
+        prioq_reshuffle(d->latest, s, &s->time.latest_index);
+        d->needs_rearm = true;
+
+        return 0;
+}
+
+_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
+        assert_return(s, -EINVAL);
+        assert_return(usec, -EINVAL);
+        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *usec = s->time.accuracy;
+        return 0;
+}
+
+_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) {
+        struct clock_data *d;
+
+        assert_return(s, -EINVAL);
+        assert_return(usec != (uint64_t) -1, -EINVAL);
+        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
+        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        if (usec == 0)
+                usec = DEFAULT_ACCURACY_USEC;
+
+        s->time.accuracy = usec;
+
+        source_set_pending(s, false);
+
+        d = event_get_clock_data(s->event, s->type);
+        assert(d);
+
+        prioq_reshuffle(d->latest, s, &s->time.latest_index);
+        d->needs_rearm = true;
+
+        return 0;
+}
+
+_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) {
+        assert_return(s, -EINVAL);
+        assert_return(clock, -EINVAL);
+        assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *clock = event_source_type_to_clock(s->type);
+        return 0;
+}
+
+_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
+        assert_return(s, -EINVAL);
+        assert_return(pid, -EINVAL);
+        assert_return(s->type == SOURCE_CHILD, -EDOM);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        *pid = s->child.pid;
+        return 0;
+}
+
+_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) {
+        int r;
+
+        assert_return(s, -EINVAL);
+        assert_return(s->type != SOURCE_EXIT, -EDOM);
+        assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(s->event), -ECHILD);
+
+        if (s->prepare == callback)
+                return 0;
+
+        if (callback && s->prepare) {
+                s->prepare = callback;
+                return 0;
+        }
+
+        r = prioq_ensure_allocated(&s->event->prepare, prepare_prioq_compare);
+        if (r < 0)
+                return r;
+
+        s->prepare = callback;
+
+        if (callback) {
+                r = prioq_put(s->event->prepare, s, &s->prepare_index);
+                if (r < 0)
+                        return r;
+        } else
+                prioq_remove(s->event->prepare, s, &s->prepare_index);
+
+        return 0;
+}
+
+_public_ void* sd_event_source_get_userdata(sd_event_source *s) {
+        assert_return(s, NULL);
+
+        return s->userdata;
+}
+
+_public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) {
+        void *ret;
+
+        assert_return(s, NULL);
+
+        ret = s->userdata;
+        s->userdata = userdata;
+
+        return ret;
+}
+
+static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) {
+        usec_t c;
+        assert(e);
+        assert(a <= b);
+
+        if (a <= 0)
+                return 0;
+        if (a >= USEC_INFINITY)
+                return USEC_INFINITY;
+
+        if (b <= a + 1)
+                return a;
+
+        initialize_perturb(e);
+
+        /*
+          Find a good time to wake up again between times a and b. We
+          have two goals here:
+
+          a) We want to wake up as seldom as possible, hence prefer
+             later times over earlier times.
+
+          b) But if we have to wake up, then let's make sure to
+             dispatch as much as possible on the entire system.
+
+          We implement this by waking up everywhere at the same time
+          within any given minute if we can, synchronised via the
+          perturbation value determined from the boot ID. If we can't,
+          then we try to find the same spot in every 10s, then 1s and
+          then 250ms step. Otherwise, we pick the last possible time
+          to wake up.
+        */
+
+        c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb;
+        if (c >= b) {
+                if (_unlikely_(c < USEC_PER_MINUTE))
+                        return b;
+
+                c -= USEC_PER_MINUTE;
+        }
+
+        if (c >= a)
+                return c;
+
+        c = (b / (USEC_PER_SEC*10)) * (USEC_PER_SEC*10) + (e->perturb % (USEC_PER_SEC*10));
+        if (c >= b) {
+                if (_unlikely_(c < USEC_PER_SEC*10))
+                        return b;
+
+                c -= USEC_PER_SEC*10;
+        }
+
+        if (c >= a)
+                return c;
+
+        c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC);
+        if (c >= b) {
+                if (_unlikely_(c < USEC_PER_SEC))
+                        return b;
+
+                c -= USEC_PER_SEC;
+        }
+
+        if (c >= a)
+                return c;
+
+        c = (b / (USEC_PER_MSEC*250)) * (USEC_PER_MSEC*250) + (e->perturb % (USEC_PER_MSEC*250));
+        if (c >= b) {
+                if (_unlikely_(c < USEC_PER_MSEC*250))
+                        return b;
+
+                c -= USEC_PER_MSEC*250;
+        }
+
+        if (c >= a)
+                return c;
+
+        return b;
+}
+
+static int event_arm_timer(
+                sd_event *e,
+                struct clock_data *d) {
+
+        struct itimerspec its = {};
+        sd_event_source *a, *b;
+        usec_t t;
+        int r;
+
+        assert(e);
+        assert(d);
+
+        if (!d->needs_rearm)
+                return 0;
+        else
+                d->needs_rearm = false;
+
+        a = prioq_peek(d->earliest);
+        if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) {
+
+                if (d->fd < 0)
+                        return 0;
+
+                if (d->next == USEC_INFINITY)
+                        return 0;
+
+                /* disarm */
+                r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL);
+                if (r < 0)
+                        return r;
+
+                d->next = USEC_INFINITY;
+                return 0;
+        }
+
+        b = prioq_peek(d->latest);
+        assert_se(b && b->enabled != SD_EVENT_OFF);
+
+        t = sleep_between(e, a->time.next, time_event_source_latest(b));
+        if (d->next == t)
+                return 0;
+
+        assert_se(d->fd >= 0);
+
+        if (t == 0) {
+                /* We don' want to disarm here, just mean some time looooong ago. */
+                its.it_value.tv_sec = 0;
+                its.it_value.tv_nsec = 1;
+        } else
+                timespec_store(&its.it_value, t);
+
+        r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL);
+        if (r < 0)
+                return -errno;
+
+        d->next = t;
+        return 0;
+}
+
+static int process_io(sd_event *e, sd_event_source *s, uint32_t revents) {
+        assert(e);
+        assert(s);
+        assert(s->type == SOURCE_IO);
+
+        /* If the event source was already pending, we just OR in the
+         * new revents, otherwise we reset the value. The ORing is
+         * necessary to handle EPOLLONESHOT events properly where
+         * readability might happen independently of writability, and
+         * we need to keep track of both */
+
+        if (s->pending)
+                s->io.revents |= revents;
+        else
+                s->io.revents = revents;
+
+        return source_set_pending(s, true);
+}
+
+static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
+        uint64_t x;
+        ssize_t ss;
+
+        assert(e);
+        assert(fd >= 0);
+
+        assert_return(events == EPOLLIN, -EIO);
+
+        ss = read(fd, &x, sizeof(x));
+        if (ss < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                return -errno;
+        }
+
+        if (_unlikely_(ss != sizeof(x)))
+                return -EIO;
+
+        if (next)
+                *next = USEC_INFINITY;
+
+        return 0;
+}
+
+static int process_timer(
+                sd_event *e,
+                usec_t n,
+                struct clock_data *d) {
+
+        sd_event_source *s;
+        int r;
+
+        assert(e);
+        assert(d);
+
+        for (;;) {
+                s = prioq_peek(d->earliest);
+                if (!s ||
+                    s->time.next > n ||
+                    s->enabled == SD_EVENT_OFF ||
+                    s->pending)
+                        break;
+
+                r = source_set_pending(s, true);
+                if (r < 0)
+                        return r;
+
+                prioq_reshuffle(d->earliest, s, &s->time.earliest_index);
+                prioq_reshuffle(d->latest, s, &s->time.latest_index);
+                d->needs_rearm = true;
+        }
+
+        return 0;
+}
+
+static int process_child(sd_event *e) {
+        sd_event_source *s;
+        Iterator i;
+        int r;
+
+        assert(e);
+
+        e->need_process_child = false;
+
+        /*
+           So, this is ugly. We iteratively invoke waitid() with P_PID
+           + WNOHANG for each PID we wait for, instead of using
+           P_ALL. This is because we only want to get child
+           information of very specific child processes, and not all
+           of them. We might not have processed the SIGCHLD even of a
+           previous invocation and we don't want to maintain a
+           unbounded *per-child* event queue, hence we really don't
+           want anything flushed out of the kernel's queue that we
+           don't care about. Since this is O(n) this means that if you
+           have a lot of processes you probably want to handle SIGCHLD
+           yourself.
+
+           We do not reap the children here (by using WNOWAIT), this
+           is only done after the event source is dispatched so that
+           the callback still sees the process as a zombie.
+        */
+
+        HASHMAP_FOREACH(s, e->child_sources, i) {
+                assert(s->type == SOURCE_CHILD);
+
+                if (s->pending)
+                        continue;
+
+                if (s->enabled == SD_EVENT_OFF)
+                        continue;
+
+                zero(s->child.siginfo);
+                r = waitid(P_PID, s->child.pid, &s->child.siginfo,
+                           WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options);
+                if (r < 0)
+                        return -errno;
+
+                if (s->child.siginfo.si_pid != 0) {
+                        bool zombie =
+                                s->child.siginfo.si_code == CLD_EXITED ||
+                                s->child.siginfo.si_code == CLD_KILLED ||
+                                s->child.siginfo.si_code == CLD_DUMPED;
+
+                        if (!zombie && (s->child.options & WEXITED)) {
+                                /* If the child isn't dead then let's
+                                 * immediately remove the state change
+                                 * from the queue, since there's no
+                                 * benefit in leaving it queued */
+
+                                assert(s->child.options & (WSTOPPED|WCONTINUED));
+                                waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
+                        }
+
+                        r = source_set_pending(s, true);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
+        bool read_one = false;
+        int r;
+
+        assert(e);
+        assert_return(events == EPOLLIN, -EIO);
+
+        /* If there's a signal queued on this priority and SIGCHLD is
+           on this priority too, then make sure to recheck the
+           children we watch. This is because we only ever dequeue
+           the first signal per priority, and if we dequeue one, and
+           SIGCHLD might be enqueued later we wouldn't know, but we
+           might have higher priority children we care about hence we
+           need to check that explicitly. */
+
+        if (sigismember(&d->sigset, SIGCHLD))
+                e->need_process_child = true;
+
+        /* If there's already an event source pending for this
+         * priority we don't read another */
+        if (d->current)
+                return 0;
+
+        for (;;) {
+                struct signalfd_siginfo si;
+                ssize_t n;
+                sd_event_source *s = NULL;
+
+                n = read(d->fd, &si, sizeof(si));
+                if (n < 0) {
+                        if (errno == EAGAIN || errno == EINTR)
+                                return read_one;
+
+                        return -errno;
+                }
+
+                if (_unlikely_(n != sizeof(si)))
+                        return -EIO;
+
+                assert(si.ssi_signo < _NSIG);
+
+                read_one = true;
+
+                if (e->signal_sources)
+                        s = e->signal_sources[si.ssi_signo];
+                if (!s)
+                        continue;
+                if (s->pending)
+                        continue;
+
+                s->signal.siginfo = si;
+                d->current = s;
+
+                r = source_set_pending(s, true);
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+}
+
+static int source_dispatch(sd_event_source *s) {
+        int r = 0;
+
+        assert(s);
+        assert(s->pending || s->type == SOURCE_EXIT);
+
+        if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) {
+                r = source_set_pending(s, false);
+                if (r < 0)
+                        return r;
+        }
+
+        if (s->type != SOURCE_POST) {
+                sd_event_source *z;
+                Iterator i;
+
+                /* If we execute a non-post source, let's mark all
+                 * post sources as pending */
+
+                SET_FOREACH(z, s->event->post_sources, i) {
+                        if (z->enabled == SD_EVENT_OFF)
+                                continue;
+
+                        r = source_set_pending(z, true);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        if (s->enabled == SD_EVENT_ONESHOT) {
+                r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
+                if (r < 0)
+                        return r;
+        }
+
+        s->dispatching = true;
+
+        switch (s->type) {
+
+        case SOURCE_IO:
+                r = s->io.callback(s, s->io.fd, s->io.revents, s->userdata);
+                break;
+
+        case SOURCE_TIME_REALTIME:
+        case SOURCE_TIME_BOOTTIME:
+        case SOURCE_TIME_MONOTONIC:
+        case SOURCE_TIME_REALTIME_ALARM:
+        case SOURCE_TIME_BOOTTIME_ALARM:
+                r = s->time.callback(s, s->time.next, s->userdata);
+                break;
+
+        case SOURCE_SIGNAL:
+                r = s->signal.callback(s, &s->signal.siginfo, s->userdata);
+                break;
+
+        case SOURCE_CHILD: {
+                bool zombie;
+
+                zombie = s->child.siginfo.si_code == CLD_EXITED ||
+                         s->child.siginfo.si_code == CLD_KILLED ||
+                         s->child.siginfo.si_code == CLD_DUMPED;
+
+                r = s->child.callback(s, &s->child.siginfo, s->userdata);
+
+                /* Now, reap the PID for good. */
+                if (zombie)
+                        waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
+
+                break;
+        }
+
+        case SOURCE_DEFER:
+                r = s->defer.callback(s, s->userdata);
+                break;
+
+        case SOURCE_POST:
+                r = s->post.callback(s, s->userdata);
+                break;
+
+        case SOURCE_EXIT:
+                r = s->exit.callback(s, s->userdata);
+                break;
+
+        case SOURCE_WATCHDOG:
+        case _SOURCE_EVENT_SOURCE_TYPE_MAX:
+        case _SOURCE_EVENT_SOURCE_TYPE_INVALID:
+                assert_not_reached("Wut? I shouldn't exist.");
+        }
+
+        s->dispatching = false;
+
+        if (r < 0)
+                log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m",
+                                strna(s->description), event_source_type_to_string(s->type));
+
+        if (s->n_ref == 0)
+                source_free(s);
+        else if (r < 0)
+                sd_event_source_set_enabled(s, SD_EVENT_OFF);
+
+        return 1;
+}
+
+static int event_prepare(sd_event *e) {
+        int r;
+
+        assert(e);
+
+        for (;;) {
+                sd_event_source *s;
+
+                s = prioq_peek(e->prepare);
+                if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF)
+                        break;
+
+                s->prepare_iteration = e->iteration;
+                r = prioq_reshuffle(e->prepare, s, &s->prepare_index);
+                if (r < 0)
+                        return r;
+
+                assert(s->prepare);
+
+                s->dispatching = true;
+                r = s->prepare(s, s->userdata);
+                s->dispatching = false;
+
+                if (r < 0)
+                        log_debug_errno(r, "Prepare callback of event source %s (type %s) returned error, disabling: %m",
+                                        strna(s->description), event_source_type_to_string(s->type));
+
+                if (s->n_ref == 0)
+                        source_free(s);
+                else if (r < 0)
+                        sd_event_source_set_enabled(s, SD_EVENT_OFF);
+        }
+
+        return 0;
+}
+
+static int dispatch_exit(sd_event *e) {
+        sd_event_source *p;
+        int r;
+
+        assert(e);
+
+        p = prioq_peek(e->exit);
+        if (!p || p->enabled == SD_EVENT_OFF) {
+                e->state = SD_EVENT_FINISHED;
+                return 0;
+        }
+
+        sd_event_ref(e);
+        e->iteration++;
+        e->state = SD_EVENT_EXITING;
+
+        r = source_dispatch(p);
+
+        e->state = SD_EVENT_INITIAL;
+        sd_event_unref(e);
+
+        return r;
+}
+
+static sd_event_source* event_next_pending(sd_event *e) {
+        sd_event_source *p;
+
+        assert(e);
+
+        p = prioq_peek(e->pending);
+        if (!p)
+                return NULL;
+
+        if (p->enabled == SD_EVENT_OFF)
+                return NULL;
+
+        return p;
+}
+
+static int arm_watchdog(sd_event *e) {
+        struct itimerspec its = {};
+        usec_t t;
+        int r;
+
+        assert(e);
+        assert(e->watchdog_fd >= 0);
+
+        t = sleep_between(e,
+                          e->watchdog_last + (e->watchdog_period / 2),
+                          e->watchdog_last + (e->watchdog_period * 3 / 4));
+
+        timespec_store(&its.it_value, t);
+
+        /* Make sure we never set the watchdog to 0, which tells the
+         * kernel to disable it. */
+        if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
+                its.it_value.tv_nsec = 1;
+
+        r = timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
+
+static int process_watchdog(sd_event *e) {
+        assert(e);
+
+        if (!e->watchdog)
+                return 0;
+
+        /* Don't notify watchdog too often */
+        if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic)
+                return 0;
+
+        sd_notify(false, "WATCHDOG=1");
+        e->watchdog_last = e->timestamp.monotonic;
+
+        return arm_watchdog(e);
+}
+
+_public_ int sd_event_prepare(sd_event *e) {
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
+
+        if (e->exit_requested)
+                goto pending;
+
+        e->iteration++;
+
+        e->state = SD_EVENT_PREPARING;
+        r = event_prepare(e);
+        e->state = SD_EVENT_INITIAL;
+        if (r < 0)
+                return r;
+
+        r = event_arm_timer(e, &e->realtime);
+        if (r < 0)
+                return r;
+
+        r = event_arm_timer(e, &e->boottime);
+        if (r < 0)
+                return r;
+
+        r = event_arm_timer(e, &e->monotonic);
+        if (r < 0)
+                return r;
+
+        r = event_arm_timer(e, &e->realtime_alarm);
+        if (r < 0)
+                return r;
+
+        r = event_arm_timer(e, &e->boottime_alarm);
+        if (r < 0)
+                return r;
+
+        if (event_next_pending(e) || e->need_process_child)
+                goto pending;
+
+        e->state = SD_EVENT_ARMED;
+
+        return 0;
+
+pending:
+        e->state = SD_EVENT_ARMED;
+        r = sd_event_wait(e, 0);
+        if (r == 0)
+                e->state = SD_EVENT_ARMED;
+
+        return r;
+}
+
+_public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
+        struct epoll_event *ev_queue;
+        unsigned ev_queue_max;
+        int r, m, i;
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
+
+        if (e->exit_requested) {
+                e->state = SD_EVENT_PENDING;
+                return 1;
+        }
+
+        ev_queue_max = MAX(e->n_sources, 1u);
+        ev_queue = newa(struct epoll_event, ev_queue_max);
+
+        m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
+                       timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC));
+        if (m < 0) {
+                if (errno == EINTR) {
+                        e->state = SD_EVENT_PENDING;
+                        return 1;
+                }
+
+                r = -errno;
+                goto finish;
+        }
+
+        dual_timestamp_get(&e->timestamp);
+        e->timestamp_boottime = now(CLOCK_BOOTTIME);
+
+        for (i = 0; i < m; i++) {
+
+                if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
+                        r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL);
+                else {
+                        WakeupType *t = ev_queue[i].data.ptr;
+
+                        switch (*t) {
+
+                        case WAKEUP_EVENT_SOURCE:
+                                r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events);
+                                break;
+
+                        case WAKEUP_CLOCK_DATA: {
+                                struct clock_data *d = ev_queue[i].data.ptr;
+                                r = flush_timer(e, d->fd, ev_queue[i].events, &d->next);
+                                break;
+                        }
+
+                        case WAKEUP_SIGNAL_DATA:
+                                r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events);
+                                break;
+
+                        default:
+                                assert_not_reached("Invalid wake-up pointer");
+                        }
+                }
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = process_watchdog(e);
+        if (r < 0)
+                goto finish;
+
+        r = process_timer(e, e->timestamp.realtime, &e->realtime);
+        if (r < 0)
+                goto finish;
+
+        r = process_timer(e, e->timestamp_boottime, &e->boottime);
+        if (r < 0)
+                goto finish;
+
+        r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
+        if (r < 0)
+                goto finish;
+
+        r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm);
+        if (r < 0)
+                goto finish;
+
+        r = process_timer(e, e->timestamp_boottime, &e->boottime_alarm);
+        if (r < 0)
+                goto finish;
+
+        if (e->need_process_child) {
+                r = process_child(e);
+                if (r < 0)
+                        goto finish;
+        }
+
+        if (event_next_pending(e)) {
+                e->state = SD_EVENT_PENDING;
+
+                return 1;
+        }
+
+        r = 0;
+
+finish:
+        e->state = SD_EVENT_INITIAL;
+
+        return r;
+}
+
+_public_ int sd_event_dispatch(sd_event *e) {
+        sd_event_source *p;
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(e->state == SD_EVENT_PENDING, -EBUSY);
+
+        if (e->exit_requested)
+                return dispatch_exit(e);
+
+        p = event_next_pending(e);
+        if (p) {
+                sd_event_ref(e);
+
+                e->state = SD_EVENT_RUNNING;
+                r = source_dispatch(p);
+                e->state = SD_EVENT_INITIAL;
+
+                sd_event_unref(e);
+
+                return r;
+        }
+
+        e->state = SD_EVENT_INITIAL;
+
+        return 1;
+}
+
+static void event_log_delays(sd_event *e) {
+        char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1];
+        unsigned i;
+        int o;
+
+        for (i = o = 0; i < ELEMENTSOF(e->delays); i++) {
+                o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]);
+                e->delays[i] = 0;
+        }
+        log_debug("Event loop iterations: %.*s", o, b);
+}
+
+_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
+
+        if (e->profile_delays && e->last_run) {
+                usec_t this_run;
+                unsigned l;
+
+                this_run = now(CLOCK_MONOTONIC);
+
+                l = u64log2(this_run - e->last_run);
+                assert(l < sizeof(e->delays));
+                e->delays[l]++;
+
+                if (this_run - e->last_log >= 5*USEC_PER_SEC) {
+                        event_log_delays(e);
+                        e->last_log = this_run;
+                }
+        }
+
+        r = sd_event_prepare(e);
+        if (r == 0)
+                /* There was nothing? Then wait... */
+                r = sd_event_wait(e, timeout);
+
+        if (e->profile_delays)
+                e->last_run = now(CLOCK_MONOTONIC);
+
+        if (r > 0) {
+                /* There's something now, then let's dispatch it */
+                r = sd_event_dispatch(e);
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+
+        return r;
+}
+
+_public_ int sd_event_loop(sd_event *e) {
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+        assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
+
+        sd_event_ref(e);
+
+        while (e->state != SD_EVENT_FINISHED) {
+                r = sd_event_run(e, (uint64_t) -1);
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = e->exit_code;
+
+finish:
+        sd_event_unref(e);
+        return r;
+}
+
+_public_ int sd_event_get_fd(sd_event *e) {
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        return e->epoll_fd;
+}
+
+_public_ int sd_event_get_state(sd_event *e) {
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        return e->state;
+}
+
+_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
+        assert_return(e, -EINVAL);
+        assert_return(code, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        if (!e->exit_requested)
+                return -ENODATA;
+
+        *code = e->exit_code;
+        return 0;
+}
+
+_public_ int sd_event_exit(sd_event *e, int code) {
+        assert_return(e, -EINVAL);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        e->exit_requested = true;
+        e->exit_code = code;
+
+        return 0;
+}
+
+_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
+        assert_return(e, -EINVAL);
+        assert_return(usec, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+        assert_return(IN_SET(clock,
+                             CLOCK_REALTIME,
+                             CLOCK_REALTIME_ALARM,
+                             CLOCK_MONOTONIC,
+                             CLOCK_BOOTTIME,
+                             CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
+
+        if (!dual_timestamp_is_set(&e->timestamp)) {
+                /* Implicitly fall back to now() if we never ran
+                 * before and thus have no cached time. */
+                *usec = now(clock);
+                return 1;
+        }
+
+        switch (clock) {
+
+        case CLOCK_REALTIME:
+        case CLOCK_REALTIME_ALARM:
+                *usec = e->timestamp.realtime;
+                break;
+
+        case CLOCK_MONOTONIC:
+                *usec = e->timestamp.monotonic;
+                break;
+
+        case CLOCK_BOOTTIME:
+        case CLOCK_BOOTTIME_ALARM:
+                *usec = e->timestamp_boottime;
+                break;
+
+        default:
+                assert_not_reached("Unknown clock?");
+        }
+
+        return 0;
+}
+
+_public_ int sd_event_default(sd_event **ret) {
+
+        static thread_local sd_event *default_event = NULL;
+        sd_event *e = NULL;
+        int r;
+
+        if (!ret)
+                return !!default_event;
+
+        if (default_event) {
+                *ret = sd_event_ref(default_event);
+                return 0;
+        }
+
+        r = sd_event_new(&e);
+        if (r < 0)
+                return r;
+
+        e->default_event_ptr = &default_event;
+        e->tid = gettid();
+        default_event = e;
+
+        *ret = e;
+        return 1;
+}
+
+#if 0 /* NM_IGNORED */
+_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
+        assert_return(e, -EINVAL);
+        assert_return(tid, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        if (e->tid != 0) {
+                *tid = e->tid;
+                return 0;
+        }
+
+        return -ENXIO;
+}
+
+_public_ int sd_event_set_watchdog(sd_event *e, int b) {
+        int r;
+
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        if (e->watchdog == !!b)
+                return e->watchdog;
+
+        if (b) {
+                struct epoll_event ev = {};
+
+                r = sd_watchdog_enabled(false, &e->watchdog_period);
+                if (r <= 0)
+                        return r;
+
+                /* Issue first ping immediately */
+                sd_notify(false, "WATCHDOG=1");
+                e->watchdog_last = now(CLOCK_MONOTONIC);
+
+                e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+                if (e->watchdog_fd < 0)
+                        return -errno;
+
+                r = arm_watchdog(e);
+                if (r < 0)
+                        goto fail;
+
+                ev.events = EPOLLIN;
+                ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG);
+
+                r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev);
+                if (r < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+
+        } else {
+                if (e->watchdog_fd >= 0) {
+                        epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL);
+                        e->watchdog_fd = safe_close(e->watchdog_fd);
+                }
+        }
+
+        e->watchdog = !!b;
+        return e->watchdog;
+
+fail:
+        e->watchdog_fd = safe_close(e->watchdog_fd);
+        return r;
+}
+
+_public_ int sd_event_get_watchdog(sd_event *e) {
+        assert_return(e, -EINVAL);
+        assert_return(!event_pid_changed(e), -ECHILD);
+
+        return e->watchdog;
+}
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
index ea09d19e..01b805a1 100644
--- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
@@ -155,7 +155,6 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_boot_id;
         static thread_local bool saved_boot_id_valid = false;
@@ -211,6 +210,7 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 _public_ int sd_id128_randomize(sd_id128_t *ret) {
         sd_id128_t t;
         int r;
diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c
index 525ce7fb..e147637b 100644
--- a/src/systemd/src/shared/dns-domain.c
+++ b/src/systemd/src/shared/dns-domain.c
@@ -182,7 +182,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
                         unsigned slashes = 0;
 
                         for (y = terminal - 1; y >= name && *y == '\\'; y--)
-                                slashes ++;
+                                slashes++;
 
                         if (slashes % 2 == 0) {
                                 /* The '.' was not escaped */
@@ -194,7 +194,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
                         }
                 }
 
-                terminal --;
+                terminal--;
         }
 
         r = dns_label_unescape(&name, dest, sz);
@@ -333,7 +333,7 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
 
         l = strlen(buffer);
 
-        /* Verify that the the result is not longer than one DNS label. */
+        /* Verify that the result is not longer than one DNS label. */
         if (l <= 0 || l > DNS_LABEL_MAX)
                 return -EINVAL;
         if (l > decoded_max)
@@ -1177,7 +1177,7 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
         assert(a);
         assert(ret);
 
-        for (; n_labels > 0; n_labels --) {
+        for (; n_labels > 0; n_labels--) {
                 r = dns_name_parent(&a);
                 if (r < 0)
                         return r;
diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h
index 2de3642c..af780f0b 100644
--- a/src/systemd/src/shared/dns-domain.h
+++ b/src/systemd/src/shared/dns-domain.h
@@ -1,3 +1,5 @@
+#pragma once
+
 /***
   This file is part of systemd.
 
@@ -17,9 +19,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
 
-#pragma once
-
-
 #include <errno.h>
 #include <stdbool.h>
 #include <stddef.h>
diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h
index 2d4e1f26..3bb886be 100644
--- a/src/systemd/src/systemd/_sd-common.h
+++ b/src/systemd/src/systemd/_sd-common.h
@@ -74,7 +74,7 @@
 #endif
 
 #define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func)             \
-        static inline void func##p(type **p) {                  \
+        static __inline__ void func##p(type **p) {              \
                 if (*p)                                         \
                         func(*p);                               \
         }                                                       \
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index 2b865a80..ef453705 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -84,9 +84,9 @@ enum {
 
 typedef struct sd_dhcp_client sd_dhcp_client;
 
-typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
+typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event,
                                     void *userdata);
-int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
+int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_callback_t cb,
                                 void *userdata);
 
 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
@@ -113,7 +113,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
 
 int sd_dhcp_client_new(sd_dhcp_client **ret);
 
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority);
+int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority);
 int sd_dhcp_client_detach_event(sd_dhcp_client *client);
 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);
 
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index 96080608..1bedc941 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -76,10 +76,10 @@ enum {
 
 typedef struct sd_dhcp6_client sd_dhcp6_client;
 
-typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event,
+typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event,
                                      void *userdata);
 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
-                                 sd_dhcp6_client_cb_t cb, void *userdata);
+                                 sd_dhcp6_client_callback_t cb, void *userdata);
 
 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
 int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
@@ -97,8 +97,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret);
 int sd_dhcp6_client_stop(sd_dhcp6_client *client);
 int sd_dhcp6_client_start(sd_dhcp6_client *client);
 int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
-int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
-                                 int priority);
+int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority);
 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h
index 1ea97e47..531ace1c 100644
--- a/src/systemd/src/systemd/sd-event.h
+++ b/src/systemd/src/systemd/sd-event.h
@@ -55,7 +55,7 @@ enum {
         SD_EVENT_RUNNING,
         SD_EVENT_EXITING,
         SD_EVENT_FINISHED,
-        SD_EVENT_PREPARING,
+        SD_EVENT_PREPARING
 };
 
 enum {
@@ -69,7 +69,11 @@ typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
 typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
 typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
 typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata);
+#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
 typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata);
+#else
+typedef void* sd_event_child_handler_t;
+#endif
 
 int sd_event_default(sd_event **e);
 
diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h
index a3bf5897..4dff0b9b 100644
--- a/src/systemd/src/systemd/sd-id128.h
+++ b/src/systemd/src/systemd/sd-id128.h
@@ -100,11 +100,11 @@ int sd_id128_get_boot(sd_id128_t *ret);
                 ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \
                 0 })
 
-_sd_pure_ static inline int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
+_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
         return memcmp(&a, &b, 16) == 0;
 }
 
-_sd_pure_ static inline int sd_id128_is_null(sd_id128_t a) {
+_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
         return a.qwords[0] == 0 && a.qwords[1] == 0;
 }
 
diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h
index 3a2219c8..9e3e14a3 100644
--- a/src/systemd/src/systemd/sd-ipv4acd.h
+++ b/src/systemd/src/systemd/sd-ipv4acd.h
@@ -37,12 +37,12 @@ enum {
 };
 
 typedef struct sd_ipv4acd sd_ipv4acd;
-typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
+typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *ll, int event, void *userdata);
 
 int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
-int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority);
 int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
-int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata);
 int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
 int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
 int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
index 67c566fe..6fa38a22 100644
--- a/src/systemd/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -36,12 +36,12 @@ enum {
 };
 
 typedef struct sd_ipv4ll sd_ipv4ll;
-typedef void (*sd_ipv4ll_cb_t)(sd_ipv4ll *ll, int event, void *userdata);
+typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata);
 
 int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
-int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority);
+int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
+int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
 int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
index ea952ef1..f7eff587 100644
--- a/src/systemd/src/systemd/sd-lldp.h
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -30,57 +30,69 @@
 
 _SD_BEGIN_DECLARATIONS;
 
-enum {
-        SD_LLDP_EVENT_UPDATE_INFO       = 0,
-};
-
-enum {
-        SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
-        SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
-        SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
-};
-
 typedef struct sd_lldp sd_lldp;
-typedef struct sd_lldp_packet sd_lldp_packet;
+typedef struct sd_lldp_neighbor sd_lldp_neighbor;
 
-typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
+typedef enum sd_lldp_event {
+        SD_LLDP_EVENT_ADDED     = 'a',
+        SD_LLDP_EVENT_REMOVED   = 'r',
+        SD_LLDP_EVENT_UPDATED   = 'u',
+        SD_LLDP_EVENT_REFRESHED = 'f',
+} sd_lldp_event;
 
-int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret);
+typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
+
+int sd_lldp_new(sd_lldp **ret, int ifindex);
 sd_lldp* sd_lldp_unref(sd_lldp *lldp);
 
 int sd_lldp_start(sd_lldp *lldp);
 int sd_lldp_stop(sd_lldp *lldp);
 
-int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority);
+int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
 int sd_lldp_detach_event(sd_lldp *lldp);
 
-int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
-int sd_lldp_save(sd_lldp *lldp, const char *file);
-
-int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
-int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
-int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
-int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
-int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
-int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
-int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
-
-/* IEEE 802.1 organizationally specific TLVs */
-int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
-int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
-int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
-int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
-int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
-
-sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
-sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
-
-int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
-
-int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);
+int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
+
+/* Controls how much and what to store in the neighbors database */
+int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
+int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask);
+int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address);
+
+int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors);
+
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
+sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
+sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
+
+/* Access to LLDP frame metadata */
+int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
+int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
+int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
+
+/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
+int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
+int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size);
+int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret);
+int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
+int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
+
+/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs
+ * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
+int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
+int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
+int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
+int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
+int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype);
+int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype);
+int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
 
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_packet, sd_lldp_packet_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
 
 _SD_END_DECLARATIONS;
 
diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h
index 76294753..29bcbe8e 100644
--- a/src/systemd/src/systemd/sd-ndisc.h
+++ b/src/systemd/src/systemd/sd-ndisc.h
@@ -52,7 +52,7 @@ int sd_ndisc_set_callback(sd_ndisc *nd,
 int sd_ndisc_set_index(sd_ndisc *nd, int interface_index);
 int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
 
-int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority);
+int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
 int sd_ndisc_detach_event(sd_ndisc *nd);
 sd_event *sd_ndisc_get_event(sd_ndisc *nd);
 
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index a3447fbf..597f477f 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -117,6 +117,7 @@ test_wired_defname_LDADD = \
 test_systemd_CFLAGS = \
 	"-I$(srcdir)/../" \
 	"-I$(srcdir)/../platform" \
+	"-I$(srcdir)/../systemd" \
 	"-I$(srcdir)/../systemd/src/systemd"
 
 test_systemd_SOURCES = \
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 9d67063e..8be2e8ce 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -867,6 +867,7 @@ test_wired_defname_LDADD = \
 test_systemd_CFLAGS = \
 	"-I$(srcdir)/../" \
 	"-I$(srcdir)/../platform" \
+	"-I$(srcdir)/../systemd" \
 	"-I$(srcdir)/../systemd/src/systemd"
 
 test_systemd_SOURCES = \
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index 19306df7..d83ff4b5 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -343,7 +343,7 @@ test_config_no_auto_default (void)
 	g_assert (!nm_config_get_no_auto_default_for_device (config, dev3));
 	g_assert (nm_config_get_no_auto_default_for_device (config, dev4));
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: update * (no-auto-default)*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: update * (no-auto-default)*");
 	nm_config_set_no_auto_default_for_device (config, dev3);
 	g_test_assert_expected_messages ();
 
@@ -556,9 +556,9 @@ _set_values_user (NMConfig *config,
 	config_data_before = g_object_ref (nm_config_get_data (config));
 
 	if (expected_changes != NM_CONFIG_CHANGE_NONE)
-		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: update *");
+		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: update *");
 	else
-		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: signal SIGHUP (no changes from disk)*");
+		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGHUP (no changes from disk)*");
 
 	nm_config_reload (config, SIGHUP);
 
@@ -600,7 +600,7 @@ _set_values_intern (NMConfig *config,
 	                  &config_changed_data);
 
 	if (expected_changes != NM_CONFIG_CHANGE_NONE)
-		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: update *");
+		g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: update *");
 
 	nm_config_set_values (config, keyfile_intern, TRUE, FALSE);
 
@@ -863,15 +863,15 @@ test_config_signal (void)
 	                  &expected);
 
 	expected = NM_CONFIG_CHANGE_SIGUSR1;
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: signal SIGUSR1");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR1");
 	nm_config_reload (config, SIGUSR1);
 
 	expected = NM_CONFIG_CHANGE_SIGUSR2;
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: signal SIGUSR2");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR2");
 	nm_config_reload (config, SIGUSR2);
 
 	expected = NM_CONFIG_CHANGE_SIGHUP;
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: signal SIGHUP (no changes from disk)*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGHUP (no changes from disk)*");
 	nm_config_reload (config, SIGHUP);
 
 
@@ -884,7 +884,7 @@ test_config_signal (void)
 	                  G_CALLBACK (_test_signal_config_changed_cb2),
 	                  &expected);
 	expected = NM_CONFIG_CHANGE_SIGUSR2;
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: signal SIGUSR2");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_INFO, "*config: signal SIGUSR2");
 	nm_config_reload (config, SIGUSR2);
 	g_signal_handlers_disconnect_by_func (config, _test_signal_config_changed_cb2, &expected);
 
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index 43595c8d..64b47bd3 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -304,7 +304,7 @@ test_nm_utils_kill_child (void)
 	test_nm_utils_kill_child_sync_do ("test-s-2", pid2s, SIGTERM, 3000, TRUE,  &expected_exit_47);
 
 	/* send invalid signal. */
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*kill child process 'test-s-3-0' (*): failed to send Unexpected signal: Invalid argument (22)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-s-3-0' (*): failed to send Unexpected signal: Invalid argument (22)");
 	test_nm_utils_kill_child_sync_do ("test-s-3-0", pid3s, -1, 0, FALSE, NULL);
 
 	/* really kill pid3s */
@@ -314,7 +314,7 @@ test_nm_utils_kill_child (void)
 
 	/* pid3s should not be a valid process, hence the call should fail. Note, that there
 	 * is a race here. */
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
 	test_nm_utils_kill_child_sync_do ("test-s-3-2", pid3s, 0, 0, FALSE, NULL);
 
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-s-4' (*): waiting up to 1 milliseconds for process to terminate normally after sending SIGTERM (15)...");
@@ -340,7 +340,7 @@ test_nm_utils_kill_child (void)
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-2' (*): invoke callback: terminated normally with status 47");
 	test_nm_utils_kill_child_async_do ("test-a-2", pid2a, SIGTERM, 3000, TRUE, &expected_exit_47);
 
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*kill child process 'test-a-3-0' (*): unexpected error sending Unexpected signal: Invalid argument (22)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-a-3-0' (*): unexpected error sending Unexpected signal: Invalid argument (22)");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-0' (*): invoke callback: killing child failed");
 	/* coverity[negative_returns] */
 	test_nm_utils_kill_child_async_do ("test-a-3-0", pid3a, -1, 1000 / 2, FALSE, NULL);
@@ -351,7 +351,7 @@ test_nm_utils_kill_child (void)
 
 	/* pid3a should not be a valid process, hence the call should fail. Note, that there
 	 * is a race here. */
-	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid (No child processes, 10) after sending no signal (0)");
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_DEBUG, "*kill child process 'test-a-3-2' (*): invoke callback: killing child failed");
 	test_nm_utils_kill_child_async_do ("test-a-3-2", pid3a, 0, 0, FALSE, NULL);
 
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index b8a51f2a..b2512590 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -1221,7 +1221,31 @@ test_nm_utils_strbuf_append (void)
 	}
 }
 
-/*******************************************/
+/*****************************************************************************/
+
+static void
+test_duplicate_decl_specifier (void)
+{
+	/* have some static variables, so that the result is certainly not optimized out. */
+	static const int v_const[1] = { 1 };
+	static int v_result[1] = { };
+	const const int v2 = 3;
+
+	/* Test that we don't get a compiler warning about duplicate const specifier.
+	 * C99 allows that and it can easily happen in macros. */
+
+#define TEST_MAX(a, b) \
+	({ \
+		const typeof(a) _a = (a); \
+		const typeof(b) _b = (b); \
+		\
+		(_a > _b ? _a : _b); \
+	})
+
+	v_result[0] = TEST_MAX (v_const[0], nmtst_get_rand_int () % 5) + v2;
+}
+
+/*****************************************************************************/
 
 NMTST_DEFINE ();
 
@@ -1254,6 +1278,7 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/general/nm_match_spec_interface_name", test_nm_match_spec_interface_name);
 	g_test_add_func ("/general/nm_match_spec_match_config", test_nm_match_spec_match_config);
+	g_test_add_func ("/general/duplicate_decl_specifier", test_duplicate_decl_specifier);
 
 	return g_test_run ();
 }
diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c
index 742f4ee5..b32775da 100644
--- a/src/tests/test-route-manager.c
+++ b/src/tests/test-route-manager.c
@@ -76,15 +76,16 @@ setup_dev1_ip4 (int ifindex)
 
 	/* Add some route outside of route manager. The route manager
 	 * should get rid of it upon sync. */
-	nm_platform_ip4_route_add (NM_PLATFORM_GET,
-	                           route.ifindex,
-	                           NM_IP_CONFIG_SOURCE_USER,
-	                           nmtst_inet4_from_string ("9.0.0.0"),
-	                           8,
-	                           INADDR_ANY,
-	                           0,
-	                           10,
-	                           route.mss);
+	if (!nm_platform_ip4_route_add (NM_PLATFORM_GET,
+	                                route.ifindex,
+	                                NM_IP_CONFIG_SOURCE_USER,
+	                                nmtst_inet4_from_string ("9.0.0.0"),
+	                                8,
+	                                INADDR_ANY,
+	                                0,
+	                                10,
+	                                route.mss))
+		g_assert_not_reached ();
 
 	route.source = NM_IP_CONFIG_SOURCE_USER;
 	inet_pton (AF_INET, "6.6.6.0", &route.network);
@@ -421,14 +422,15 @@ setup_dev1_ip6 (int ifindex)
 
 	/* Add some route outside of route manager. The route manager
 	 * should get rid of it upon sync. */
-	nm_platform_ip6_route_add (NM_PLATFORM_GET,
-	                           ifindex,
-	                           NM_IP_CONFIG_SOURCE_USER,
-	                           *nmtst_inet6_from_string ("2001:db8:8088::"),
-	                           48,
-	                           in6addr_any,
-	                           10,
-	                           0);
+	if (!nm_platform_ip6_route_add (NM_PLATFORM_GET,
+	                                ifindex,
+	                                NM_IP_CONFIG_SOURCE_USER,
+	                                *nmtst_inet6_from_string ("2001:db8:8088::"),
+	                                48,
+	                                in6addr_any,
+	                                10,
+	                                0))
+		g_assert_not_reached ();
 
 	route = nmtst_platform_ip6_route_full ("2001:db8:8086::",
 	                                       48,
diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c
index 84ad95ba..936bff20 100644
--- a/src/tests/test-systemd.c
+++ b/src/tests/test-systemd.c
@@ -19,7 +19,11 @@
 
 #include "nm-default.h"
 
+#include "nm-sd.h"
+
 #include "sd-dhcp-client.h"
+#include "sd-lldp.h"
+#include "sd-event.h"
 
 #include "nm-test-utils.h"
 
@@ -40,6 +44,87 @@ test_dhcp_create (void)
 
 /*****************************************************************************/
 
+static void
+test_lldp_create (void)
+{
+	sd_lldp *lldp = NULL;
+	int ifindex = 1;
+	int r;
+
+	r = sd_lldp_new (&lldp, ifindex);
+	g_assert (r == 0);
+	g_assert (lldp);
+
+	sd_lldp_unref (lldp);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	GMainLoop *mainloop;
+	sd_event_source *event_source;
+} TestSdEventData;
+
+static int
+_test_sd_event_timeout_cb (sd_event_source *s, uint64_t usec, void *userdata)
+{
+	TestSdEventData *user_data = userdata;
+
+	g_assert (user_data);
+	g_assert (user_data->mainloop);
+	g_assert (user_data->event_source);
+
+	user_data->event_source = sd_event_source_unref (user_data->event_source);
+	g_main_loop_quit (user_data->mainloop);
+	return 0;
+}
+
+static void
+test_sd_event (void)
+{
+	int repeat;
+
+	for (repeat = 0; repeat < 2; repeat++) {
+		guint sd_id = 0;
+		int r;
+		int i, n;
+		sd_event *other_events[3] = { NULL }, *event = NULL;
+		TestSdEventData user_data = { 0 };
+
+		g_assert_cmpint (sd_event_default (NULL), ==, 0);
+
+		for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) {
+			r = sd_event_default (&other_events[i]);
+			g_assert (r >= 0 && other_events[i]);
+		}
+
+		sd_id = nm_sd_event_attach_default ();
+
+		r = sd_event_default (&event);
+		g_assert (r >= 0 && event);
+
+		r = sd_event_add_time (event, &user_data.event_source, CLOCK_MONOTONIC, 1, 0, _test_sd_event_timeout_cb, &user_data);
+		g_assert (r >= 0 && user_data.event_source);
+
+		user_data.mainloop = g_main_loop_new (NULL, FALSE);
+		g_main_loop_run (user_data.mainloop);
+		g_main_loop_unref (user_data.mainloop);
+
+		g_assert (!user_data.event_source);
+
+		event = sd_event_unref (event);
+		for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++)
+			other_events[i] = sd_event_unref (other_events[i]);
+		nm_clear_g_source (&sd_id);
+		for (i = 0, n = G_N_ELEMENTS (other_events); i < n; i++)
+			other_events[i] = sd_event_unref (other_events[i]);
+
+		g_assert_cmpint (sd_event_default (NULL), ==, 0);
+	}
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -48,6 +133,8 @@ main (int argc, char **argv)
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "ALL");
 
 	g_test_add_func ("/systemd/dhcp/create", test_dhcp_create);
+	g_test_add_func ("/systemd/lldp/create", test_lldp_create);
+	g_test_add_func ("/systemd/sd-event", test_sd_event);
 
 	return g_test_run ();
 }
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 653b9972..cbc7c69d 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -196,7 +196,7 @@ __LOG_create_prefix (char *buf, NMVpnConnection *self)
 	            "]",
 	            _NMLOG_PREFIX_NAME,
 	            self,
-	            con ? "," : "--", con ? str_if_set (nm_connection_get_uuid (con), "??") : "",
+	            con ? "," : "--", con ? (nm_connection_get_uuid (con) ?: "??") : "",
 	            con ? "," : "", NM_PRINT_FMT_QUOTED (id, "\"", id, "\"", con ? "??" : ""),
 	            priv->ip_ifindex,
 	            priv->ip_iface ? ":" : "", NM_PRINT_FMT_QUOTED (priv->ip_iface, "(", priv->ip_iface, ")", "")
@@ -319,14 +319,25 @@ _get_applied_connection (NMVpnConnection *connection)
 }
 
 static void
+disconnect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+{
+	g_dbus_proxy_call_finish (proxy, result, NULL);
+	g_object_unref (user_data);
+}
+
+static void
 call_plugin_disconnect (NMVpnConnection *self)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	if (priv->proxy) {
-		g_dbus_proxy_call (priv->proxy, "Disconnect", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
-		g_clear_object (&priv->proxy);
-	}
+	g_dbus_proxy_call (priv->proxy,
+	                   "Disconnect",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   priv->cancellable,
+	                   (GAsyncReadyCallback) disconnect_cb,
+	                   g_object_ref (self));
 }
 
 static void
@@ -1934,7 +1945,7 @@ nm_vpn_service_daemon_exec (NMVpnConnection *self, GError **error)
 
 	if (success) {
 		_LOGI ("Started the VPN service, PID %ld", (long int) pid);
-		priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, g_object_ref (self));
+		priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, self);
 	} else {
 		g_set_error (error,
 		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
@@ -2224,8 +2235,8 @@ get_secrets_cb (NMSettingsConnection *connection,
 		return;
 
 	if (error && priv->secrets_idx >= SECRETS_REQ_NEW) {
-		_LOGE ("Failed to request VPN secrets #%d: (%d) %s",
-		       priv->secrets_idx + 1, error->code, error->message);
+		_LOGE ("Failed to request VPN secrets #%d: %s",
+		       priv->secrets_idx + 1, error->message);
 		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 		return;
 	}
@@ -2390,6 +2401,8 @@ dispose (GObject *object)
 	NMVpnConnection *self = NM_VPN_CONNECTION (object);
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
+	nm_clear_g_source (&priv->start_timeout);
+
 	g_clear_pointer (&priv->connect_hash, g_variant_unref);
 
 	nm_clear_g_source (&priv->connect_timeout);