summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManagerUtils.c88
-rw-r--r--src/NetworkManagerUtils.h2
-rw-r--r--src/devices/nm-device-ethernet.c60
-rw-r--r--src/devices/nm-device-infiniband.c2
-rw-r--r--src/devices/nm-device-logging.h10
-rw-r--r--src/devices/nm-device-macvlan.c5
-rw-r--r--src/devices/nm-device-vlan.c41
-rw-r--r--src/devices/nm-device.c438
-rw-r--r--src/devices/nm-device.h3
-rw-r--r--src/devices/team/nm-device-team.c21
-rw-r--r--src/devices/wifi/nm-device-wifi.c39
-rw-r--r--src/devices/wifi/nm-wifi-ap.c25
-rw-r--r--src/devices/wifi/nm-wifi-ap.h1
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c21
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c2
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c7
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.c3
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h2
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h1
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c52
-rw-r--r--src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h1
-rw-r--r--src/dhcp-manager/tests/test-dhcp-utils.c60
-rw-r--r--src/main.c8
-rw-r--r--src/nm-access-point-glue.h2
-rw-r--r--src/nm-active-connection.c67
-rw-r--r--src/nm-active-connection.h6
-rw-r--r--src/nm-auth-manager.c20
-rw-r--r--src/nm-connectivity.c58
-rw-r--r--src/nm-default-route-manager.c32
-rw-r--r--src/nm-device-glue.h2
-rw-r--r--src/nm-enum-types.c20
-rw-r--r--src/nm-enum-types.h2
-rw-r--r--src/nm-iface-helper.c5
-rw-r--r--src/nm-ip4-config.c183
-rw-r--r--src/nm-ip4-config.h9
-rw-r--r--src/nm-ip6-config.c66
-rw-r--r--src/nm-ip6-config.h3
-rw-r--r--src/nm-logging.h31
-rw-r--r--src/nm-manager-glue.h2
-rw-r--r--src/nm-manager.c138
-rw-r--r--src/nm-manager.h4
-rw-r--r--src/nm-route-manager.c45
-rw-r--r--src/nm-types.h6
-rw-r--r--src/org.freedesktop.NetworkManager.conf2
-rw-r--r--src/platform/nm-linux-platform.c106
-rw-r--r--src/platform/nm-platform-utils.c62
-rw-r--r--src/platform/nm-platform-utils.h6
-rw-r--r--src/platform/nm-platform.c54
-rw-r--r--src/platform/nm-platform.h7
-rw-r--r--src/platform/nmp-object.c28
-rw-r--r--src/platform/tests/test-link.c5
-rw-r--r--src/platform/wifi/wifi-utils-wext.c4
-rw-r--r--src/settings/nm-settings-connection.c422
-rw-r--r--src/settings/nm-settings-connection.h68
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c27
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c15
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c87
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am3
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in3
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c125
-rw-r--r--src/settings/plugins/ifcfg-rh/writer.c114
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c66
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c10
-rw-r--r--src/tests/config/nm-test-device.c16
-rw-r--r--src/tests/test-general-with-expect.c39
-rw-r--r--src/tests/test-ip4-config.c4
-rw-r--r--src/tests/test-route-manager.c2
-rw-r--r--src/vpn-manager/nm-vpn-connection.c4
71 files changed, 2101 insertions, 801 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 4f842494..63741dc3 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -2169,15 +2169,15 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection
 /**************************************************************************/
 
 static gint64 monotonic_timestamp_offset_sec;
+static int monotonic_timestamp_clock_mode = 0;
 
 static void
 monotonic_timestamp_get (struct timespec *tp)
 {
-	static int clock_mode = 0;
-	gboolean first_time = FALSE;
+	int clock_mode = 0;
 	int err = 0;
 
-	switch (clock_mode) {
+	switch (monotonic_timestamp_clock_mode) {
 	case 0:
 		/* the clock is not yet initialized (first run) */
 		err = clock_gettime (CLOCK_BOOTTIME, tp);
@@ -2186,7 +2186,6 @@ monotonic_timestamp_get (struct timespec *tp)
 			err = clock_gettime (CLOCK_MONOTONIC, tp);
 		} else
 			clock_mode = 1;
-		first_time = TRUE;
 		break;
 	case 1:
 		/* default, return CLOCK_BOOTTIME */
@@ -2202,7 +2201,7 @@ monotonic_timestamp_get (struct timespec *tp)
 	g_assert (err == 0); (void)err;
 	g_assert (tp->tv_nsec >= 0 && tp->tv_nsec < NM_UTILS_NS_PER_SECOND);
 
-	if (G_LIKELY (!first_time))
+	if (G_LIKELY (clock_mode == 0))
 		return;
 
 	/* Calculate an offset for the time stamp.
@@ -2219,6 +2218,7 @@ monotonic_timestamp_get (struct timespec *tp)
 	 * wraps (~68 years).
 	 **/
 	monotonic_timestamp_offset_sec = (- ((gint64) tp->tv_sec)) + 1;
+	monotonic_timestamp_clock_mode = clock_mode;
 
 	if (nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
 		time_t now = time (NULL);
@@ -2532,11 +2532,15 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base,
 
 			if (print_header) {
 				GError *err_verify = NULL;
+				const char *path = nm_connection_get_path (connection);
 
-				if (diff_base)
-					nm_log (level, domain, "%sconnection '%s' (%p/%s < %p/%s):", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base));
-				else
-					nm_log (level, domain, "%sconnection '%s' (%p/%s):", prefix, name, connection, G_OBJECT_TYPE_NAME (connection));
+				if (diff_base) {
+					nm_log (level, domain, "%sconnection '%s' (%p/%s < %p/%s)%s%s%s:", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base),
+					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
+				} else {
+					nm_log (level, domain, "%sconnection '%s' (%p/%s):%s%s%s", prefix, name, connection, G_OBJECT_TYPE_NAME (connection),
+					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
+				}
 				print_header = FALSE;
 
 				if (!nm_connection_verify (connection, &err_verify)) {
@@ -2583,6 +2587,49 @@ out:
 	g_array_free (sorted_hashes, TRUE);
 }
 
+/**
+ * nm_utils_monotonic_timestamp_as_boottime:
+ * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME.
+ * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if
+ * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano
+ * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc.
+ *
+ * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime().
+ * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick.
+ * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds.
+ * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp
+ * is always positive.
+ *
+ * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC.
+ **/
+gint64
+nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick)
+{
+	gint64 offset;
+
+	/* only support ns-per-tick being a multiple of 10. */
+	g_return_val_if_fail (timestamp_ns_per_tick == 1
+	                      || (timestamp_ns_per_tick > 0 &&
+	                          timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND &&
+	                          timestamp_ns_per_tick % 10 == 0),
+	                      -1);
+
+	/* Check that the timestamp is in a valid range. */
+	g_return_val_if_fail (timestamp >= 0, -1);
+
+	/* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful
+	 * value (because he has no idea what these timestamps would be). That would be a bug. */
+	g_return_val_if_fail (monotonic_timestamp_clock_mode != 0, -1);
+
+	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
+	offset = monotonic_timestamp_offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick);
+
+	/* check for overflow. */
+	g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64);
+
+	return timestamp - offset;
+}
+
 
 #define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
 #define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
@@ -2638,28 +2685,35 @@ nm_utils_ip4_property_path (const char *ifname, const char *property)
 	return _get_property_path (ifname, property, FALSE);
 }
 
-const char *
-ASSERT_VALID_PATH_COMPONENT (const char *name)
+gboolean
+nm_utils_is_valid_path_component (const char *name)
 {
 	const char *n;
 
 	if (name == NULL || name[0] == '\0')
-		goto fail;
+		return FALSE;
 
 	if (name[0] == '.') {
 		if (name[1] == '\0')
-			goto fail;
+			return FALSE;
 		if (name[1] == '.' && name[2] == '\0')
-			goto fail;
+			return FALSE;
 	}
 	n = name;
 	do {
 		if (*n == '/')
-			goto fail;
+			return FALSE;
 	} while (*(++n) != '\0');
 
-	return name;
-fail:
+	return TRUE;
+}
+
+const char *
+ASSERT_VALID_PATH_COMPONENT (const char *name)
+{
+	if (G_LIKELY (nm_utils_is_valid_path_component (name)))
+		return name;
+
 	nm_log_err (LOGD_CORE, "Failed asserting path component: %s%s%s",
 	            NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)"));
 	g_error ("FATAL: Failed asserting path component: %s%s%s",
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 1864547f..70f62d47 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -161,7 +161,9 @@ gint64 nm_utils_get_monotonic_timestamp_ns (void);
 gint64 nm_utils_get_monotonic_timestamp_us (void);
 gint64 nm_utils_get_monotonic_timestamp_ms (void);
 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_utils_ip6_property_path (const char *ifname, const char *property);
 const char *nm_utils_ip4_property_path (const char *ifname, const char *property);
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index af2cb8f2..beb86e11 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -53,6 +53,7 @@
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
+#include "gsystem-local-alloc.h"
 
 #include "nm-device-ethernet-glue.h"
 
@@ -66,6 +67,7 @@ G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
 #define WIRED_SECRETS_TRIES "wired-secrets-tries"
 
 #define PPPOE_RECONNECT_DELAY 7
+#define PPPOE_ENCAP_OVERHEAD  8 /* 2 bytes for PPP, 6 for PPPoE */
 
 static NMSetting *device_get_setting (NMDevice *device, GType setting_type);
 
@@ -1174,6 +1176,40 @@ dcb_carrier_changed (NMDevice *device, GParamSpec *pspec, gpointer unused)
 
 /****************************************************************/
 
+static gboolean
+wake_on_lan_enable (NMDevice *device)
+{
+	NMSettingWiredWakeOnLan wol;
+	NMSettingWired *s_wired;
+	const char *password = NULL;
+	gs_free char *value = NULL;
+
+	s_wired = (NMSettingWired *) device_get_setting (device, NM_TYPE_SETTING_WIRED);
+	if (s_wired) {
+		wol = nm_setting_wired_get_wake_on_lan (s_wired);
+		password = nm_setting_wired_get_wake_on_lan_password (s_wired);
+		if (wol != NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)
+			goto found;
+	}
+
+	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                               "ethernet.wake-on-lan",
+	                                               device);
+	if (value) {
+		wol = _nm_utils_ascii_str_to_int64 (value, 10,
+		                                    NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
+		                                    NM_SETTING_WIRED_WAKE_ON_LAN_ALL,
+		                                    NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT);
+		if (wol != NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)
+			goto found;
+	}
+	wol = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
+found:
+	return nmp_utils_ethtool_set_wake_on_lan (nm_device_get_iface (device), wol, password);
+}
+
+/****************************************************************/
+
 static NMActStageReturn
 act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 {
@@ -1206,6 +1242,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		}
 	}
 
+	wake_on_lan_enable (device);
+
 	/* DCB and FCoE setup */
 	s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB);
 	if (s_dcb) {
@@ -1231,6 +1269,28 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	}
 
+	/* PPPoE setup */
+	if (nm_connection_is_type (nm_device_get_connection (device),
+	                           NM_SETTING_PPPOE_SETTING_NAME)) {
+		NMSettingPpp *s_ppp;
+
+		s_ppp = (NMSettingPpp *) device_get_setting (device, NM_TYPE_SETTING_PPP);
+		if (s_ppp) {
+			guint32 mtu = 0, mru = 0, mxu;
+
+			mtu = nm_setting_ppp_get_mtu (s_ppp);
+			mru = nm_setting_ppp_get_mru (s_ppp);
+			mxu = mru > mtu ? mru : mtu;
+			if (mxu) {
+				_LOGD (LOGD_PPP, "set MTU to %u (PPP interface MRU %u, MTU %u)",
+				       mxu + PPPOE_ENCAP_OVERHEAD, mru, mtu);
+				nm_platform_link_set_mtu (NM_PLATFORM_GET,
+				                          nm_device_get_ifindex (device),
+				                          mxu + PPPOE_ENCAP_OVERHEAD);
+			}
+		}
+	}
+
 	return ret;
 }
 
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 2e2483c0..2d519f61 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -313,7 +313,7 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore,
 	                                  NM_DEVICE_PLATFORM_DEVICE, plink,
 	                                  NM_DEVICE_TYPE_DESC, "InfiniBand",
 	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND,
-	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, (plink->parent > 0),
+	                                  NM_DEVICE_INFINIBAND_IS_PARTITION, (plink->parent > 0 || plink->parent == NM_PLATFORM_LINK_OTHER_NETNS),
 	                                  NULL);
 }
 
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index acca32e5..f3b76ee8 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -31,16 +31,12 @@ _nm_device_log_self_to_device (t *self) \
     return (NMDevice *) self; \
 }
 
-#define _LOG(level, domain, ...) \
+#undef  _NMLOG_ENABLED
+#define _NMLOG_ENABLED(level, domain) ( nm_logging_enabled ((level), (domain)) )
+#define _NMLOG(level, domain, ...) \
     nm_log_obj ((level), (domain), (self), \
                 "(%s): " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 (self) ? str_if_set (nm_device_get_iface (_nm_device_log_self_to_device (self)), "(null)") : "(none)" \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__))
 
-#define _LOGT(domain, ...)      _LOG (LOGL_TRACE, domain, __VA_ARGS__)
-#define _LOGD(domain, ...)      _LOG (LOGL_DEBUG, domain, __VA_ARGS__)
-#define _LOGI(domain, ...)      _LOG (LOGL_INFO,  domain, __VA_ARGS__)
-#define _LOGW(domain, ...)      _LOG (LOGL_WARN,  domain, __VA_ARGS__)
-#define _LOGE(domain, ...)      _LOG (LOGL_ERR,   domain, __VA_ARGS__)
-
 #endif /* __NETWORKMANAGER_DEVICE_LOGGING_H__ */
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index 0bfe3fe9..2a815cfd 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -114,7 +114,10 @@ get_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);
+		if (priv->props.parent_ifindex > 0)
+			parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex);
+		else
+			parent = NULL;
 		g_value_set_boxed (value, parent ? nm_device_get_path (parent) : "/");
 		break;
 	case PROP_MODE:
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 158e7de5..e523e6bb 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -181,7 +181,8 @@ component_added (NMDevice *device, GObject *component)
 		return FALSE;
 	}
 
-	if (nm_device_get_ifindex (added_device) != parent_ifindex)
+	if (   parent_ifindex <= 0
+	    || nm_device_get_ifindex (added_device) != parent_ifindex)
 		return FALSE;
 
 	nm_device_vlan_set_parent (self, added_device, FALSE);
@@ -356,23 +357,28 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (vlan_id != nm_setting_vlan_get_id (s_vlan))
 		g_object_set (s_vlan, NM_SETTING_VLAN_ID, priv->vlan_id, NULL);
 
-	parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
-	g_assert (parent);
+	if (parent_ifindex != NM_PLATFORM_LINK_OTHER_NETNS)
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+	else
+		parent = NULL;
 	nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent, FALSE);
 
 	/* Update parent in the connection; default to parent's interface name */
-	new_parent = nm_device_get_iface (parent);
-	setting_parent = nm_setting_vlan_get_parent (s_vlan);
-	if (setting_parent && nm_utils_is_uuid (setting_parent)) {
-		NMConnection *parent_connection;
-
-		/* Don't change a parent specified by UUID if it's still valid */
-		parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
-		if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
-			new_parent = NULL;
-	}
-	if (new_parent)
-		g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL);
+	if (parent) {
+		new_parent = nm_device_get_iface (parent);
+		setting_parent = nm_setting_vlan_get_parent (s_vlan);
+		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
+			NMConnection *parent_connection;
+
+			/* Don't change a parent specified by UUID if it's still valid */
+			parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
+			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
+				new_parent = NULL;
+		}
+		if (new_parent)
+			g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL);
+	} else
+		g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, NULL, NULL);
 }
 
 static NMActStageReturn
@@ -628,7 +634,10 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore,
 		                     "VLAN parent ifindex unknown");
 		return NULL;
 	}
-	parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+	if (parent_ifindex > 0)
+		parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
+	else
+		parent = NULL;
 
 	device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
 	                                    NM_DEVICE_PLATFORM_DEVICE, plink,
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index c5d4c203..58895821 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -75,6 +75,10 @@ _LOG_DECLARE_SELF (NMDevice);
 static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context);
 static void impl_device_delete     (NMDevice *self, DBusGMethodInvocation *context);
 static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
+static gboolean ip_config_valid (NMDeviceState state);
+static void nm_device_update_metered (NMDevice *self);
+static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason);
+static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason);
 
 #include "nm-device-glue.h"
 
@@ -128,6 +132,7 @@ enum {
 	PROP_MASTER,
 	PROP_HW_ADDRESS,
 	PROP_HAS_PENDING_ACTION,
+	PROP_METERED,
 	LAST_PROP
 };
 
@@ -269,18 +274,20 @@ typedef struct {
 	struct {
 		gboolean v4_has;
 		gboolean v4_is_assumed;
-		gboolean v4_configure_first_time;
 		NMPlatformIP4Route v4;
 		gboolean v6_has;
 		gboolean v6_is_assumed;
-		gboolean v6_configure_first_time;
 		NMPlatformIP6Route v6;
 	} default_route;
 
+	gboolean v4_commit_first_time;
+	gboolean v6_commit_first_time;
+
 	/* DHCPv4 tracking */
 	NMDhcpClient *  dhcp4_client;
 	gulong          dhcp4_state_sigid;
 	NMDhcp4Config * dhcp4_config;
+	guint           dhcp4_restart_id;
 	NMIP4Config *   vpn4_config;  /* routes added by a VPN which uses this device */
 
 	guint           arp_round2_id;
@@ -325,6 +332,9 @@ typedef struct {
 	NMDhcp6Config * dhcp6_config;
 	/* IP6 config from DHCP */
 	NMIP6Config *   dhcp6_ip6_config;
+	/* Event ID of the current IP6 config from DHCP */
+	char *          dhcp6_event_id;
+	guint           dhcp6_restart_id;
 
 	/* allow autoconnect feature */
 	gboolean        autoconnect;
@@ -338,6 +348,8 @@ typedef struct {
 	gboolean        is_master;
 	GSList *        slaves;    /* list of SlaveInfo */
 
+	NMMetered       metered;
+
 	NMConnectionProvider *con_provider;
 } NMDevicePrivate;
 
@@ -687,6 +699,21 @@ nm_device_get_device_type (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->type;
 }
 
+/**
+ * nm_device_get_metered:
+ * @setting: the #NMDevice
+ *
+ * Returns: the #NMDevice:metered property of the device.
+ *
+ * Since: 1.0.6
+ **/
+NMMetered
+nm_device_get_metered (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_METERED_UNKNOWN);
+
+	return NM_DEVICE_GET_PRIVATE (self)->metered;
+}
 
 /**
  * nm_device_get_priority():
@@ -735,14 +762,14 @@ nm_device_get_priority (NMDevice *self)
 		return 400;
 	case NM_DEVICE_TYPE_BRIDGE:
 		return 425;
-	case NM_DEVICE_TYPE_MODEM:
-		return 450;
-	case NM_DEVICE_TYPE_BT:
-		return 550;
 	case NM_DEVICE_TYPE_WIFI:
 		return 600;
 	case NM_DEVICE_TYPE_OLPC_MESH:
 		return 650;
+	case NM_DEVICE_TYPE_MODEM:
+		return 700;
+	case NM_DEVICE_TYPE_BT:
+		return 750;
 	case NM_DEVICE_TYPE_GENERIC:
 		return 950;
 	case NM_DEVICE_TYPE_UNKNOWN:
@@ -3181,6 +3208,8 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	nm_clear_g_source (&priv->dhcp4_restart_id);
+
 	if (priv->dhcp4_client) {
 		/* Stop any ongoing DHCP transaction on this device */
 		if (priv->dhcp4_state_sigid) {
@@ -3218,6 +3247,8 @@ ip4_config_merge_and_apply (NMDevice *self,
 	guint32 gateway;
 	gboolean connection_has_default_route, connection_is_never_default;
 	gboolean routes_full_sync;
+	gboolean ignore_auto_routes = FALSE;
+	gboolean ignore_auto_dns = FALSE;
 
 	/* Merge all the configs into the composite config */
 	if (config) {
@@ -3225,44 +3256,45 @@ ip4_config_merge_and_apply (NMDevice *self,
 		priv->dev_ip4_config = g_object_ref (config);
 	}
 
+	/* Apply ignore-auto-routes and ignore-auto-dns settings */
+	connection = nm_device_get_connection (self);
+	if (connection) {
+		NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection);
+
+		if (s_ip4) {
+			ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4);
+			ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip4);
+		}
+	}
+
 	composite = nm_ip4_config_new ();
 
 	if (commit)
 		ensure_con_ip4_config (self);
 
-	if (priv->dev_ip4_config)
-		nm_ip4_config_merge (composite, priv->dev_ip4_config);
+	if (priv->dev_ip4_config) {
+		nm_ip4_config_merge (composite, priv->dev_ip4_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
+	}
 	if (priv->vpn4_config)
-		nm_ip4_config_merge (composite, priv->vpn4_config);
+		nm_ip4_config_merge (composite, priv->vpn4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 	if (priv->ext_ip4_config)
-		nm_ip4_config_merge (composite, priv->ext_ip4_config);
+		nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
 	/* Merge WWAN config *last* to ensure modem-given settings overwrite
 	 * any external stuff set by pppd or other scripts.
 	 */
-	if (priv->wwan_ip4_config)
-		nm_ip4_config_merge (composite, priv->wwan_ip4_config);
-
-	/* Apply ignore-auto-routes and ignore-auto-dns settings */
-	connection = nm_device_get_connection (self);
-	if (connection) {
-		NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection);
-
-		if (s_ip4) {
-			if (nm_setting_ip_config_get_ignore_auto_routes (s_ip4))
-				nm_ip4_config_reset_routes (composite);
-			if (nm_setting_ip_config_get_ignore_auto_dns (s_ip4)) {
-				nm_ip4_config_reset_nameservers (composite);
-				nm_ip4_config_reset_domains (composite);
-				nm_ip4_config_reset_searches (composite);
-			}
-		}
+	if (priv->wwan_ip4_config) {
+		nm_ip4_config_merge (composite, priv->wwan_ip4_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
 	}
 
 	/* Merge user overrides into the composite config. For assumed connections,
 	 * con_ip4_config is empty. */
 	if (priv->con_ip4_config)
-		nm_ip4_config_merge (composite, priv->con_ip4_config);
+		nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
 
 	/* Add the default route.
@@ -3282,21 +3314,26 @@ ip4_config_merge_and_apply (NMDevice *self,
 	priv->default_route.v4_has = FALSE;
 	priv->default_route.v4_is_assumed = TRUE;
 
-	routes_full_sync =    commit
-	                   && priv->default_route.v4_configure_first_time
-	                   && !nm_device_uses_assumed_connection (self);
-
 	if (!commit) {
 		/* during a non-commit event, we always pickup whatever is configured. */
 		goto END_ADD_DEFAULT_ROUTE;
 	}
 
+	if (nm_device_uses_generated_assumed_connection (self)) {
+		/* a generate-assumed-connection always detects the default route from platform */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	/* At this point, we treat assumed and non-assumed connections alike.
+	 * For assumed connections we do that because we still manage RA and DHCP
+	 * leases for them, so we must extend/update the default route on commits.
+	 */
+
 	connection_has_default_route
 	    = nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (),
 	                                                                 connection, &connection_is_never_default);
 
-	if (   !priv->default_route.v4_configure_first_time
-	    && !nm_device_uses_assumed_connection (self)
+	if (   !priv->v4_commit_first_time
 	    && connection_is_never_default) {
 		/* If the connection is explicitly configured as never-default, we enforce the (absense of the)
 		 * default-route only once. That allows the user to configure a connection as never-default,
@@ -3304,14 +3341,8 @@ ip4_config_merge_and_apply (NMDevice *self,
 		goto END_ADD_DEFAULT_ROUTE;
 	}
 
-	/* At this point, we treat assumed and non-assumed connections alike.
-	 * For assumed connections we do that because we still manage RA and DHCP
-	 * leases for them, so we must extend/update the default route on commits.
-	 */
-
 	/* we are about to commit (for a non-assumed connection). Enforce whatever we have
 	 * configured. */
-	priv->default_route.v4_configure_first_time = FALSE;
 	priv->default_route.v4_is_assumed = FALSE;
 
 	if (!connection_has_default_route)
@@ -3323,7 +3354,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 	}
 
 	gateway = nm_ip4_config_get_gateway (composite);
-	if (   !gateway
+	if (   !nm_ip4_config_has_gateway (composite)
 	    && nm_device_get_device_type (self) != NM_DEVICE_TYPE_MODEM)
 		goto END_ADD_DEFAULT_ROUTE;
 
@@ -3365,8 +3396,15 @@ END_ADD_DEFAULT_ROUTE:
 			NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite);
 	}
 
+	routes_full_sync =    commit
+	                   && priv->v4_commit_first_time
+	                   && !nm_device_uses_assumed_connection (self);
+
 	success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync, out_reason);
 	g_object_unref (composite);
+
+	if (commit)
+		priv->v4_commit_first_time = FALSE;
 	return success;
 }
 
@@ -3391,12 +3429,44 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 	}
 }
 
+static gboolean
+dhcp4_restart_cb (gpointer user_data)
+{
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
+	NMDeviceStateReason reason;
+	NMConnection *connection;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	priv->dhcp4_restart_id = 0;
+	connection = nm_device_get_connection (self);
+
+	if (dhcp4_start (self, connection, &reason) == NM_ACT_STAGE_RETURN_FAILURE)
+		priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self);
+
+	return FALSE;
+}
+
 static void
 dhcp4_fail (NMDevice *self, gboolean timeout)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
+
+	/* Don't fail if there are static addresses configured on
+	 * the device, instead retry after some time.
+	 */
+	if (   priv->ip4_state == IP_DONE
+	    && priv->con_ip4_config
+	    && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) {
+		_LOGI (LOGD_DHCP4, "Scheduling DHCPv4 restart because device has IP addresses");
+		priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self);
+		return;
+	}
+
 	if (timeout || (priv->ip4_state == IP_CONF))
 		nm_device_activate_schedule_ip4_config_timeout (self);
 	else if (priv->ip4_state == IP_DONE)
@@ -3426,6 +3496,7 @@ dhcp4_state_changed (NMDhcpClient *client,
                      NMDhcpState state,
                      NMIP4Config *ip4_config,
                      GHashTable *options,
+                     const char *event_id,
                      gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
@@ -3450,8 +3521,10 @@ dhcp4_state_changed (NMDhcpClient *client,
 
 		if (priv->ip4_state == IP_CONF)
 			nm_device_activate_schedule_ip4_config_result (self, ip4_config);
-		else if (priv->ip4_state == IP_DONE)
+		else if (priv->ip4_state == IP_DONE) {
 			dhcp4_lease_change (self, ip4_config);
+			nm_device_update_metered (self);
+		}
 		break;
 	case NM_DHCP_STATE_TIMEOUT:
 		dhcp4_fail (self, TRUE);
@@ -3802,6 +3875,9 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 
 	priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE;
 	g_clear_object (&priv->dhcp6_ip6_config);
+	g_clear_pointer (&priv->dhcp6_event_id, g_free);
+
+	nm_clear_g_source (&priv->dhcp6_restart_id);
 
 	if (priv->dhcp6_client) {
 		if (priv->dhcp6_state_sigid) {
@@ -3837,6 +3913,19 @@ ip6_config_merge_and_apply (NMDevice *self,
 	const struct in6_addr *gateway;
 	gboolean connection_has_default_route, connection_is_never_default;
 	gboolean routes_full_sync;
+	gboolean ignore_auto_routes = FALSE;
+	gboolean ignore_auto_dns = FALSE;
+
+	/* Apply ignore-auto-routes and ignore-auto-dns settings */
+	connection = nm_device_get_connection (self);
+	if (connection) {
+		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
+
+		if (s_ip6) {
+			ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip6);
+			ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip6);
+		}
+	}
 
 	/* If no config was passed in, create a new one */
 	composite = nm_ip6_config_new ();
@@ -3845,41 +3934,34 @@ ip6_config_merge_and_apply (NMDevice *self,
 		ensure_con_ip6_config (self);
 
 	/* Merge all the IP configs into the composite config */
-	if (priv->ac_ip6_config)
-		nm_ip6_config_merge (composite, priv->ac_ip6_config);
-	if (priv->dhcp6_ip6_config)
-		nm_ip6_config_merge (composite, priv->dhcp6_ip6_config);
+	if (priv->ac_ip6_config) {
+		nm_ip6_config_merge (composite, priv->ac_ip6_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
+	}
+	if (priv->dhcp6_ip6_config) {
+		nm_ip6_config_merge (composite, priv->dhcp6_ip6_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
+	}
 	if (priv->vpn6_config)
-		nm_ip6_config_merge (composite, priv->vpn6_config);
+		nm_ip6_config_merge (composite, priv->vpn6_config, NM_IP_CONFIG_MERGE_DEFAULT);
 	if (priv->ext_ip6_config)
-		nm_ip6_config_merge (composite, priv->ext_ip6_config);
+		nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
 	/* Merge WWAN config *last* to ensure modem-given settings overwrite
 	 * any external stuff set by pppd or other scripts.
 	 */
-	if (priv->wwan_ip6_config)
-		nm_ip6_config_merge (composite, priv->wwan_ip6_config);
-
-	/* Apply ignore-auto-routes and ignore-auto-dns settings */
-	connection = nm_device_get_connection (self);
-	if (connection) {
-		NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection);
-
-		if (s_ip6) {
-			if (nm_setting_ip_config_get_ignore_auto_routes (s_ip6))
-				nm_ip6_config_reset_routes (composite);
-			if (nm_setting_ip_config_get_ignore_auto_dns (s_ip6)) {
-				nm_ip6_config_reset_nameservers (composite);
-				nm_ip6_config_reset_domains (composite);
-				nm_ip6_config_reset_searches (composite);
-			}
-		}
+	if (priv->wwan_ip6_config) {
+		nm_ip6_config_merge (composite, priv->wwan_ip6_config,
+		                       (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
+		                     | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
 	}
 
 	/* Merge user overrides into the composite config. For assumed connections,
 	 * con_ip6_config is empty. */
 	if (priv->con_ip6_config)
-		nm_ip6_config_merge (composite, priv->con_ip6_config);
+		nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
 
 	/* Add the default route.
 	 *
@@ -3898,21 +3980,26 @@ ip6_config_merge_and_apply (NMDevice *self,
 	priv->default_route.v6_has = FALSE;
 	priv->default_route.v6_is_assumed = TRUE;
 
-	routes_full_sync =    commit
-	                   && priv->default_route.v6_configure_first_time
-	                   && !nm_device_uses_assumed_connection (self);
-
 	if (!commit) {
 		/* during a non-commit event, we always pickup whatever is configured. */
 		goto END_ADD_DEFAULT_ROUTE;
 	}
 
+	if (nm_device_uses_generated_assumed_connection (self)) {
+		/* a generate-assumed-connection always detects the default route from platform */
+		goto END_ADD_DEFAULT_ROUTE;
+	}
+
+	/* At this point, we treat assumed and non-assumed connections alike.
+	 * For assumed connections we do that because we still manage RA and DHCP
+	 * leases for them, so we must extend/update the default route on commits.
+	 */
+
 	connection_has_default_route
 	    = nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (),
 	                                                                 connection, &connection_is_never_default);
 
-	if (   !priv->default_route.v6_configure_first_time
-	    && !nm_device_uses_assumed_connection (self)
+	if (   !priv->v6_commit_first_time
 	    && connection_is_never_default) {
 		/* If the connection is explicitly configured as never-default, we enforce the (absence of the)
 		 * default-route only once. That allows the user to configure a connection as never-default,
@@ -3920,14 +4007,8 @@ ip6_config_merge_and_apply (NMDevice *self,
 		goto END_ADD_DEFAULT_ROUTE;
 	}
 
-	/* At this point, we treat assumed and non-assumed connections alike.
-	 * For assumed connections we do that because we still manage RA and DHCP
-	 * leases for them, so we must extend/update the default route on commits.
-	 */
-
 	/* we are about to commit (for a non-assumed connection). Enforce whatever we have
 	 * configured. */
-	priv->default_route.v6_configure_first_time = FALSE;
 	priv->default_route.v6_is_assumed = FALSE;
 
 	if (!connection_has_default_route)
@@ -3984,8 +4065,14 @@ END_ADD_DEFAULT_ROUTE:
 			NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite);
 	}
 
+	routes_full_sync =    commit
+	                   && priv->v6_commit_first_time
+	                   && !nm_device_uses_assumed_connection (self);
+
 	success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync, out_reason);
 	g_object_unref (composite);
+	if (commit)
+		priv->v6_commit_first_time = FALSE;
 	return success;
 }
 
@@ -4017,6 +4104,24 @@ dhcp6_lease_change (NMDevice *self)
 	}
 }
 
+static gboolean
+dhcp6_restart_cb (gpointer user_data)
+{
+	NMDevice *self = user_data;
+	NMDevicePrivate *priv;
+	NMDeviceStateReason reason;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	priv->dhcp6_restart_id = 0;
+
+	if (!dhcp6_start (self, FALSE, &reason))
+		priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self);
+
+	return FALSE;
+}
+
 static void
 dhcp6_fail (NMDevice *self, gboolean timeout)
 {
@@ -4025,6 +4130,17 @@ dhcp6_fail (NMDevice *self, gboolean timeout)
 	dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 
 	if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) {
+		/* Don't fail if there are static addresses configured on
+		 * the device, instead retry after some time.
+		 */
+		if (   priv->ip6_state == IP_DONE
+		    && priv->con_ip6_config
+		    && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) {
+			_LOGI (LOGD_DHCP6, "Scheduling DHCPv6 restart because device has IP addresses");
+			priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self);
+			return;
+		}
+
 		if (timeout || (priv->ip6_state == IP_CONF))
 			nm_device_activate_schedule_ip6_config_timeout (self);
 		else if (priv->ip6_state == IP_DONE)
@@ -4074,10 +4190,12 @@ dhcp6_state_changed (NMDhcpClient *client,
                      NMDhcpState state,
                      NMIP6Config *ip6_config,
                      GHashTable *options,
+                     const char *event_id,
                      gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	guint i;
 
 	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
 	g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config));
@@ -4086,10 +4204,27 @@ dhcp6_state_changed (NMDhcpClient *client,
 
 	switch (state) {
 	case NM_DHCP_STATE_BOUND:
-		g_clear_object (&priv->dhcp6_ip6_config);
-		if (ip6_config) {
-			priv->dhcp6_ip6_config = g_object_ref (ip6_config);
-			dhcp6_update_config (self, priv->dhcp6_config, options);
+		/* If the server sends multiple IPv6 addresses, we receive a state
+		 * changed event for each of them. Use the event ID to merge IPv6
+		 * addresses from the same transaction into a single configuration.
+		 */
+		if (   ip6_config
+		    && event_id
+		    && priv->dhcp6_event_id
+		    && !strcmp (event_id, priv->dhcp6_event_id)) {
+			for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
+				nm_ip6_config_add_address (priv->dhcp6_ip6_config,
+				                           nm_ip6_config_get_address (ip6_config, i));
+			}
+		} else {
+			g_clear_object (&priv->dhcp6_ip6_config);
+			g_clear_pointer (&priv->dhcp6_event_id, g_free);
+			if (ip6_config) {
+				priv->dhcp6_ip6_config = g_object_ref (ip6_config);
+				priv->dhcp6_event_id = g_strdup (event_id);
+				dhcp6_update_config (self, priv->dhcp6_config, options);
+				g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
+			}
 		}
 
 		if (priv->ip6_state == IP_CONF) {
@@ -4182,6 +4317,7 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
 
 	g_warn_if_fail (priv->dhcp6_ip6_config == NULL);
 	g_clear_object (&priv->dhcp6_ip6_config);
+	g_clear_pointer (&priv->dhcp6_event_id, g_free);
 
 	connection = nm_device_get_connection (self);
 	g_assert (connection);
@@ -4437,7 +4573,7 @@ static void
 nm_device_set_mtu (NMDevice *self, guint32 mtu)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	int ifindex = nm_device_get_ifindex (self);
+	int ifindex = nm_device_get_ip_ifindex (self);
 
 	if (mtu)
 		priv->mtu = mtu;
@@ -4446,7 +4582,7 @@ nm_device_set_mtu (NMDevice *self, guint32 mtu)
 	if (priv->ip6_mtu)
 		nm_device_ipv6_set_mtu (self, priv->ip6_mtu);
 
-	if (priv->mtu != nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex))
+	if (priv->mtu && priv->mtu != nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex))
 		nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, priv->mtu);
 }
 
@@ -4459,20 +4595,20 @@ nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu)
 
 	priv->ip6_mtu = mtu ?: plat_mtu;
 
-	if (priv->ip6_mtu && priv->mtu < priv->ip6_mtu) {
-		_LOGW (LOGD_DEVICE | LOGD_IP6, "Lowering IPv6 MTU (%d) to match device MTU (%d)",
+	if (priv->ip6_mtu && priv->mtu && priv->mtu < priv->ip6_mtu) {
+		_LOGI (LOGD_DEVICE | LOGD_IP6, "Lowering IPv6 MTU (%d) to match device MTU (%d)",
 		       priv->ip6_mtu, priv->mtu);
 		priv->ip6_mtu = priv->mtu;
 	}
 
-	if (priv->ip6_mtu < 1280) {
-		_LOGW (LOGD_DEVICE | LOGD_IP6, "IPv6 MTU (%d) smaller than 1280, adjusting",
+	if (priv->ip6_mtu && priv->ip6_mtu < 1280) {
+		_LOGI (LOGD_DEVICE | LOGD_IP6, "IPv6 MTU (%d) smaller than 1280, adjusting",
 		       priv->ip6_mtu);
 		priv->ip6_mtu = 1280;
 	}
 
-	if (priv->mtu < priv->ip6_mtu) {
-		_LOGW (LOGD_DEVICE | LOGD_IP6, "Raising device MTU (%d) to match IPv6 MTU (%d)",
+	if (priv->ip6_mtu && priv->mtu && priv->mtu < priv->ip6_mtu) {
+		_LOGI (LOGD_DEVICE | LOGD_IP6, "Raising device MTU (%d) to match IPv6 MTU (%d)",
 		       priv->mtu, priv->ip6_mtu);
 		nm_device_set_mtu (self, priv->ip6_mtu);
 	}
@@ -5010,11 +5146,6 @@ act_stage3_ip6_config_start (NMDevice *self,
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
 		ret = linklocal6_start (self);
-		if (ret == NM_ACT_STAGE_RETURN_FINISH) {
-			/* New blank config; LL address is already in priv->ext_ip6_config */
-			*out_config = nm_ip6_config_new ();
-			g_assert (*out_config);
-		}
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
 		priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_MANAGED;
 		if (!dhcp6_start (self, TRUE, reason)) {
@@ -5903,26 +6034,19 @@ static void
 _update_ip4_address (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	struct ifreq req;
-	guint32 new_address;
-	int fd;
-
-	g_return_if_fail (self  != NULL);
+	guint32 addr;
 
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		_LOGE (LOGD_IP4, "couldn't open control socket.");
-		return;
-	}
+	g_return_if_fail (NM_IS_DEVICE (self));
 
-	memset (&req, 0, sizeof (struct ifreq));
-	strncpy (req.ifr_name, nm_device_get_ip_iface (self), IFNAMSIZ);
-	if (ioctl (fd, SIOCGIFADDR, &req) == 0) {
-		new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr;
-		if (new_address != priv->ip4_address)
-			priv->ip4_address = new_address;
+	if (   priv->ip4_config
+	    && ip_config_valid (priv->state)
+	    && nm_ip4_config_get_num_addresses (priv->ip4_config)) {
+		addr = nm_ip4_config_get_address (priv->ip4_config, 0)->address;
+		if (addr != priv->ip4_address) {
+			priv->ip4_address = addr;
+			g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS);
+		}
 	}
-	close (fd);
 }
 
 gboolean
@@ -7497,6 +7621,59 @@ nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr)
 	priv->dhcp_anycast_address = g_strdup (addr);
 }
 
+static void
+nm_device_update_metered (NMDevice *self)
+{
+#define NM_METERED_INVALID ((NMMetered) -1)
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMSettingConnection *setting;
+	NMMetered conn_value, value = NM_METERED_INVALID;
+	NMConnection *connection = NULL;
+	NMDeviceState state;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	state = nm_device_get_state (self);
+	if (   state <= NM_DEVICE_STATE_DISCONNECTED
+	    || state > NM_DEVICE_STATE_ACTIVATED)
+		value = NM_METERED_UNKNOWN;
+
+	if (value == NM_METERED_INVALID) {
+		connection = nm_device_get_connection (self);
+		if (connection) {
+			setting = nm_connection_get_setting_connection (connection);
+			if (setting) {
+				conn_value = nm_setting_connection_get_metered (setting);
+				if (conn_value != NM_METERED_UNKNOWN)
+					value = conn_value;
+			}
+		}
+	}
+
+	/* Try to guess a value using the metered flag in IP configuration */
+	if (value == NM_METERED_INVALID) {
+		if (   priv->ip4_config
+		    && priv->ip4_state == IP_DONE
+		    && nm_ip4_config_get_metered (priv->ip4_config))
+			value = NM_METERED_GUESS_YES;
+	}
+
+	/* Otherwise look at connection type */
+	if (value == NM_METERED_INVALID) {
+		if (   nm_connection_is_type (connection, NM_SETTING_GSM_SETTING_NAME)
+		    || nm_connection_is_type (connection, NM_SETTING_CDMA_SETTING_NAME))
+			value = NM_METERED_GUESS_YES;
+		else
+			value = NM_METERED_GUESS_NO;
+	}
+
+	if (value != priv->metered) {
+		_LOGD (LOGD_DEVICE, "set metered value %d", value);
+		priv->metered = value;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_METERED);
+	}
+}
+
 /**
  * nm_device_check_connection_available():
  * @self: the #NMDevice
@@ -7856,10 +8033,11 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 
 	priv->default_route.v4_has = FALSE;
 	priv->default_route.v4_is_assumed = TRUE;
-	priv->default_route.v4_configure_first_time = TRUE;
 	priv->default_route.v6_has = FALSE;
 	priv->default_route.v6_is_assumed = TRUE;
-	priv->default_route.v6_configure_first_time = TRUE;
+
+	priv->v4_commit_first_time = TRUE;
+	priv->v6_commit_first_time = TRUE;
 
 	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
 	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
@@ -7943,9 +8121,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 	nm_device_master_release_slaves (self);
 
 	/* slave: mark no longer enslaved */
-	g_clear_object (&priv->master);
-	priv->enslaved = FALSE;
-	g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
+	if (nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0) {
+		g_clear_object (&priv->master);
+		priv->enslaved = FALSE;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER);
+	}
 
 	/* Take out any entries in the routing table and any IP address the device had. */
 	ifindex = nm_device_get_ip_ifindex (self);
@@ -7954,6 +8134,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 		nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
 	}
 
+	nm_device_update_metered (self);
 	_cleanup_generic_post (self, cleanup_type);
 }
 
@@ -8426,6 +8607,7 @@ _set_state_full (NMDevice *self,
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
 		_LOGI (LOGD_DEVICE, "Activation: successful, device activated.");
+		nm_device_update_metered (self);
 		nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL);
 		break;
 	case NM_DEVICE_STATE_FAILED:
@@ -8799,9 +8981,10 @@ nm_device_init (NMDevice *self)
 	priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
 
 	priv->default_route.v4_is_assumed = TRUE;
-	priv->default_route.v4_configure_first_time = TRUE;
 	priv->default_route.v6_is_assumed = TRUE;
-	priv->default_route.v6_configure_first_time = TRUE;
+
+	priv->v4_commit_first_time = TRUE;
+	priv->v6_commit_first_time = TRUE;
 }
 
 static GObject*
@@ -9298,6 +9481,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_HAS_PENDING_ACTION:
 		g_value_set_boolean (value, nm_device_has_pending_action (self));
 		break;
+	case PROP_METERED:
+		g_value_set_uint (value, priv->metered);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -9561,6 +9747,20 @@ nm_device_class_init (NMDeviceClass *klass)
 		                       G_PARAM_READABLE |
 		                       G_PARAM_STATIC_STRINGS));
 
+	/**
+	 * NMDevice:metered:
+	 *
+	 * Whether the connection is metered.
+	 *
+	 * Since: 1.0.6
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_METERED,
+		 g_param_spec_uint (NM_DEVICE_METERED, "", "",
+		                    0, G_MAXUINT32, NM_METERED_UNKNOWN,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
 	/* Signals */
 	signals[STATE_CHANGED] =
 		g_signal_new ("state-changed",
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index f1dab9bf..e9d5b948 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -58,6 +58,7 @@
 #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id"
 #define NM_DEVICE_MTU              "mtu"
 #define NM_DEVICE_HW_ADDRESS       "hw-address"
+#define NM_DEVICE_METERED          "metered"
 
 #define NM_DEVICE_TYPE_DESC        "type-desc"      /* Internal only */
 #define NM_DEVICE_RFKILL_TYPE      "rfkill-type"    /* Internal only */
@@ -74,7 +75,6 @@
 #define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate"
 #define NM_DEVICE_RECHECK_ASSUME        "recheck-assume"
 
-
 G_BEGIN_DECLS
 
 #define NM_TYPE_DEVICE			(nm_device_get_type ())
@@ -281,6 +281,7 @@ const char *	nm_device_get_driver_version	(NMDevice *dev);
 const char *	nm_device_get_type_desc (NMDevice *dev);
 const char *	nm_device_get_type_description (NMDevice *dev);
 NMDeviceType	nm_device_get_device_type	(NMDevice *dev);
+NMMetered       nm_device_get_metered	(NMDevice *dev);
 
 int			nm_device_get_priority (NMDevice *dev);
 guint32     nm_device_get_ip4_route_metric (NMDevice *dev);
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 23750f35..09b2dd7d 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -40,6 +40,7 @@
 #include "nm-enum-types.h"
 #include "nm-team-enum-types.h"
 #include "nm-core-internal.h"
+#include "nm-ip4-config.h"
 #include "gsystem-local-alloc.h"
 
 #include "nm-device-team-glue.h"
@@ -565,6 +566,25 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 }
 
 static void
+ip4_config_pre_commit (NMDevice *self, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	guint32 mtu;
+
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+	s_wired = nm_connection_get_setting_wired (connection);
+
+	if (s_wired) {
+		/* MTU override */
+		mtu = nm_setting_wired_get_mtu (s_wired);
+		if (mtu)
+			nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
+	}
+}
+
+static void
 deactivate (NMDevice *device)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
@@ -813,6 +833,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->master_update_slave_connection = master_update_slave_connection;
 
 	parent_class->act_stage1_prepare = act_stage1_prepare;
+	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
 	parent_class->deactivate = deactivate;
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 2e116f87..dde743b1 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -55,6 +55,7 @@
 #include "nm-dbus-glib-types.h"
 #include "nm-wifi-enum-types.h"
 #include "nm-connection-provider.h"
+#include "gsystem-local-alloc.h"
 
 
 static gboolean impl_device_get_access_points (NMDeviceWifi *device,
@@ -178,7 +179,7 @@ static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
 
 static void schedule_scanlist_cull (NMDeviceWifi *self);
 
-static gboolean request_wireless_scan (gpointer user_data);
+static void request_wireless_scan (NMDeviceWifi *self, GHashTable *scan_options);
 
 static void remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap);
 
@@ -753,7 +754,7 @@ deactivate (NMDevice *device)
 	/* Ensure we trigger a scan after deactivating a Hotspot */
 	if (old_mode == NM_802_11_MODE_AP) {
 		cancel_pending_scan (self);
-		request_wireless_scan (self);
+		request_wireless_scan (self, NULL);
 	}
 }
 
@@ -1253,6 +1254,7 @@ request_scan_cb (NMDevice *device,
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	GError *local = NULL;
+	gs_unref_hashtable GHashTable *new_scan_options = user_data;
 
 	if (error) {
 		dbus_g_method_return_error (context, error);
@@ -1269,7 +1271,7 @@ request_scan_cb (NMDevice *device,
 	}
 
 	cancel_pending_scan (self);
-	request_wireless_scan (self);
+	request_wireless_scan (self, new_scan_options);
 	dbus_g_method_return (context);
 }
 
@@ -1316,7 +1318,7 @@ impl_device_request_scan (NMDeviceWifi *self,
 	                       NM_AUTH_PERMISSION_NETWORK_CONTROL,
 	                       TRUE,
 	                       request_scan_cb,
-	                       NULL);
+	                       options ? g_hash_table_ref (options) : NULL);
 	return;
 
 error:
@@ -1482,23 +1484,31 @@ build_hidden_probe_list (NMDeviceWifi *self)
 	return ssids;
 }
 
-static gboolean
-request_wireless_scan (gpointer user_data)
+static void
+request_wireless_scan (NMDeviceWifi *self, GHashTable *scan_options)
 {
-	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	gboolean backoff = FALSE;
 	GPtrArray *ssids = NULL;
 
 	if (priv->requested_scan) {
 		/* There's already a scan in progress */
-		return FALSE;
+		return;
 	}
 
 	if (check_scanning_allowed (self)) {
 		_LOGD (LOGD_WIFI_SCAN, "scanning requested");
 
-		ssids = build_hidden_probe_list (self);
+		if (scan_options && g_hash_table_size (scan_options)) {
+			GValue *val = g_hash_table_lookup (scan_options, "ssids");
+
+			if (val && G_VALUE_HOLDS (val, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR))
+				ssids = g_ptr_array_ref (g_value_get_boxed (val));
+			else
+				_LOGD (LOGD_WIFI_SCAN, "ignoring invalid scan options");
+		}
+		if (!ssids)
+			ssids = build_hidden_probe_list (self);
 
 		if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) {
 			if (ssids) {
@@ -1533,9 +1543,14 @@ request_wireless_scan (gpointer user_data)
 
 	priv->pending_scan_id = 0;
 	schedule_scan (self, backoff);
-	return FALSE;
 }
 
+static gboolean
+request_wireless_scan_periodic (gpointer user_data)
+{
+	request_wireless_scan (user_data, NULL);
+	return FALSE;
+}
 
 /*
  * schedule_scan
@@ -1563,7 +1578,7 @@ schedule_scan (NMDeviceWifi *self, gboolean backoff)
 			factor = 1;
 
 		priv->pending_scan_id = g_timeout_add_seconds (next_scan,
-		                                               request_wireless_scan,
+		                                               request_wireless_scan_periodic,
 		                                               self);
 
 		priv->scheduled_scan_time = now + priv->scan_interval;
@@ -3072,7 +3087,7 @@ device_state_changed (NMDevice *device,
 		/* Kick off a scan to get latest results */
 		priv->scan_interval = SCAN_INTERVAL_MIN;
 		cancel_pending_scan (self);
-		request_wireless_scan (self);
+		request_wireless_scan (self, NULL);
 		break;
 	default:
 		break;
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index d70dd9a6..1485f4c1 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -79,6 +79,7 @@ enum {
 	PROP_MODE,
 	PROP_MAX_BITRATE,
 	PROP_STRENGTH,
+	PROP_LAST_SEEN,
 	LAST_PROP
 };
 
@@ -195,6 +196,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_STRENGTH:
 		g_value_set_schar (value, priv->strength);
 		break;
+	case PROP_LAST_SEEN:
+		g_value_set_int (value,
+		                 priv->last_seen > 0
+		                     ? (gint) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
+		                     : -1);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -294,6 +301,13 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
 		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 		                    G_PARAM_STATIC_STRINGS));
 
+	g_object_class_install_property
+		(object_class, PROP_LAST_SEEN,
+		 g_param_spec_int (NM_AP_LAST_SEEN, "", "",
+		                   -1, G_MAXINT, -1,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (ap_class),
 	                                        &dbus_glib_nm_access_point_object_info);
@@ -1056,7 +1070,7 @@ void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast)
 gint32
 nm_ap_get_last_seen (const NMAccessPoint *ap)
 {
-	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
 
 	return NM_AP_GET_PRIVATE (ap)->last_seen;
 }
@@ -1064,9 +1078,16 @@ nm_ap_get_last_seen (const NMAccessPoint *ap)
 void
 nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen)
 {
+	NMAccessPointPrivate *priv;
+
 	g_return_if_fail (NM_IS_AP (ap));
 
-	NM_AP_GET_PRIVATE (ap)->last_seen = last_seen;
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->last_seen != last_seen) {
+		priv->last_seen = last_seen;
+		g_object_notify (G_OBJECT (ap), NM_AP_LAST_SEEN);
+	}
 }
 
 gboolean
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 8ad9acb7..67d30769 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -43,6 +43,7 @@
 #define NM_AP_MODE "mode"
 #define NM_AP_MAX_BITRATE "max-bitrate"
 #define NM_AP_STRENGTH "strength"
+#define NM_AP_LAST_SEEN "last-seen"
 
 typedef struct {
 	GObject parent;
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index eb317330..7f5d5a28 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -36,6 +36,7 @@
 #include "nm-dhcp-client.h"
 #include "nm-dhcp-utils.h"
 #include "nm-platform.h"
+#include "gsystem-local-alloc.h"
 
 typedef struct {
 	char *       iface;
@@ -284,6 +285,7 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
                           GHashTable *options)
 {
 	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+	gs_free char *event_id = NULL;
 
 	if (new_state >= NM_DHCP_STATE_BOUND)
 		timeout_cleanup (self);
@@ -308,19 +310,30 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 	if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
 		return;
 
+	if (priv->ipv6 && new_state == NM_DHCP_STATE_BOUND) {
+		char *start, *iaid;
+
+		iaid = g_hash_table_lookup (options, "iaid");
+		start = g_hash_table_lookup (options, "life_starts");
+		if (iaid && start)
+			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): 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));
+	             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),
 	               signals[SIGNAL_STATE_CHANGED], 0,
 	               new_state,
 	               ip_config,
-	               options);
+	               options,
+	               event_id);
 }
 
 static gboolean
@@ -977,6 +990,6 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 					  G_SIGNAL_RUN_FIRST,
 					  G_STRUCT_OFFSET (NMDhcpClientClass, state_changed),
 					  NULL, NULL, NULL,
-					  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE);
+					  G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING);
 }
 
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index f936f45c..20ddefc3 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -184,6 +184,7 @@ static void client_state_changed (NMDhcpClient *client,
                                   NMDhcpState state,
                                   GObject *ip_config,
                                   GHashTable *options,
+                                  const char *event_id,
                                   NMDhcpManager *self);
 
 static void
@@ -204,6 +205,7 @@ client_state_changed (NMDhcpClient *client,
                       NMDhcpState state,
                       GObject *ip_config,
                       GHashTable *options,
+                      const char *event_id,
                       NMDhcpManager *self)
 {
 	if (state >= NM_DHCP_STATE_TIMEOUT)
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
index 12dc03cc..2bd0d72f 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.c
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -224,6 +224,8 @@ lease_to_ip4_config (sd_dhcp_lease *lease,
 	guint16 mtu;
 	int r, num;
 	guint64 end_time;
+	uint8_t *data;
+	gboolean metered = FALSE;
 
 	g_return_val_if_fail (lease != NULL, NULL);
 
@@ -355,6 +357,11 @@ lease_to_ip4_config (sd_dhcp_lease *lease,
 		g_string_free (l, TRUE);
 	}
 
+	num = sd_dhcp_lease_get_vendor_specific (lease, &data);
+	if (num > 0)
+		metered = !!memmem (data, num, "ANDROID_METERED", STRLEN ("ANDROID_METERED"));
+	nm_ip4_config_set_metered (ip4_config, metered);
+
 	return ip4_config;
 }
 
diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c
index 8cd4359a..ab7f26d5 100644
--- a/src/dhcp-manager/nm-dhcp-utils.c
+++ b/src/dhcp-manager/nm-dhcp-utils.c
@@ -575,6 +575,9 @@ nm_dhcp_utils_ip4_config_from_options (const char *iface,
 		g_strfreev (nis);
 	}
 
+	str = g_hash_table_lookup (options, "vendor_encapsulated_options");
+	nm_ip4_config_set_metered (ip4_config, str && strstr (str, "ANDROID_METERED"));
+
 	return ip4_config;
 
 error:
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h
index 9e184ac4..71f7f143 100644
--- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h
@@ -72,6 +72,8 @@ struct sd_dhcp_lease {
         char *root_path;
         uint8_t *client_id;
         size_t client_id_len;
+        uint8_t *vendor_specific;
+        size_t vendor_specific_size;
 };
 
 int dhcp_lease_new(sd_dhcp_lease **ret);
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h
index abca9422..aa37e9b0 100644
--- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h
@@ -125,6 +125,7 @@ enum {
         DHCP_OPTION_BROADCAST                   = 28,
         DHCP_OPTION_STATIC_ROUTE                = 33,
         DHCP_OPTION_NTP_SERVER                  = 42,
+        DHCP_OPTION_VENDOR_SPECIFIC             = 43,
         DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
         DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
         DHCP_OPTION_OVERLOAD                    = 52,
diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c
index 2d13d503..0b5048a6 100644
--- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c
@@ -191,6 +191,19 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes
         return 0;
 }
 
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, uint8_t **data) {
+        assert_return(lease, -EINVAL);
+        assert_return(data, -EINVAL);
+
+        if (lease->vendor_specific) {
+                *data = lease->vendor_specific;
+                return lease->vendor_specific_size;
+        } else
+                return -ENOENT;
+
+        return 0;
+}
+
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
         if (lease)
                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
@@ -286,6 +299,24 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
         return 0;
 }
 
+static int lease_parse_binary(const uint8_t *option, size_t len, uint8_t **ret) {
+        assert (option);
+        assert (ret);
+
+        if (len >= 1) {
+                uint8_t *data;
+
+                data = memdup(option, len);
+                if (!data)
+                        return -errno;
+
+                free(*ret);
+                *ret = data;
+        }
+
+        return 0;
+}
+
 static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
         assert(option);
         assert(ret);
@@ -568,6 +599,14 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
                         return r;
 
                 break;
+
+        case DHCP_OPTION_VENDOR_SPECIFIC:
+               r = lease_parse_binary(option, len, &lease->vendor_specific);
+               if (r < 0)
+                       return r;
+               lease->vendor_specific_size = len;
+
+               break;
         }
 
         return 0;
@@ -595,6 +634,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         const uint8_t *client_id;
         size_t client_id_len;
         const char *string;
+        uint8_t *data;
         uint16_t mtu;
         struct sd_dhcp_route *routes;
         int r;
@@ -667,6 +707,18 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         if (r >= 0)
                 serialize_dhcp_routes(f, "ROUTES", routes, r);
 
+        r = sd_dhcp_lease_get_vendor_specific(lease, &data);
+        if (r >= 0) {
+                _cleanup_free_ char *option_hex = NULL;
+
+                option_hex = hexmem(data, r);
+                if (!option_hex) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+                fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
+        }
+
         r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
         if (r >= 0) {
                 _cleanup_free_ char *client_id_hex = NULL;
diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h
index 4296b91d..079a1bb3 100644
--- a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h
+++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h
@@ -45,6 +45,7 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
 int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn);
+int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, uint8_t **data);
 int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id,
                                 size_t *client_id_len);
 
diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c
index 53688a5b..618188df 100644
--- a/src/dhcp-manager/tests/test-dhcp-utils.c
+++ b/src/dhcp-manager/tests/test-dhcp-utils.c
@@ -72,7 +72,7 @@ static void
 test_generic_options (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const NMPlatformIP4Address *address;
 	const NMPlatformIP4Route *route;
 	guint32 tmp;
@@ -147,7 +147,7 @@ static void
 test_wins_options (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const NMPlatformIP4Address *address;
 	guint32 tmp;
 	const char *expected_wins1 = "63.12.199.5";
@@ -176,6 +176,31 @@ test_wins_options (void)
 }
 
 static void
+test_vendor_option_metered (void)
+{
+	GHashTable *options;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
+	static const Option data[] = {
+		{ "vendor_encapsulated_options", "ANDROID_METERED" },
+		{ NULL, NULL }
+	};
+
+	options = fill_table (generic_options, NULL);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_assert (nm_ip4_config_get_metered (ip4_config) == FALSE);
+	g_hash_table_destroy (options);
+	g_clear_object (&ip4_config);
+
+	options = fill_table (generic_options, NULL);
+	options = fill_table (data, options);
+	ip4_config = nm_dhcp_utils_ip4_config_from_options ("eth0", options, 0);
+	g_assert (ip4_config);
+	g_assert (nm_ip4_config_get_metered (ip4_config) == TRUE);
+	g_hash_table_destroy (options);
+}
+
+static void
 ip4_test_route (NMIP4Config *ip4_config,
                 guint route_num,
                 const char *expected_dest,
@@ -208,7 +233,7 @@ static void
 test_classless_static_routes_1 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	const char *expected_route2_dest = "10.0.0.0";
@@ -236,7 +261,7 @@ static void
 test_classless_static_routes_2 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	const char *expected_route2_dest = "10.0.0.0";
@@ -264,7 +289,7 @@ static void
 test_fedora_dhclient_classless_static_routes (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "129.210.177.128";
 	const char *expected_route1_gw = "192.168.0.113";
 	const char *expected_route2_dest = "2.0.0.0";
@@ -296,7 +321,7 @@ static void
 test_dhclient_invalid_classless_routes_1 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	static const Option data[] = {
@@ -325,7 +350,7 @@ static void
 test_dhcpcd_invalid_classless_routes_1 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "10.1.1.5";
 	const char *expected_route1_gw = "10.1.1.1";
 	const char *expected_route2_dest = "100.99.88.56";
@@ -359,7 +384,7 @@ static void
 test_dhclient_invalid_classless_routes_2 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "10.1.1.5";
 	const char *expected_route1_gw = "10.1.1.1";
 	const char *expected_route2_dest = "100.99.88.56";
@@ -392,7 +417,7 @@ static void
 test_dhcpcd_invalid_classless_routes_2 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "10.1.1.5";
 	const char *expected_route1_gw = "10.1.1.1";
 	const char *expected_route2_dest = "100.99.88.56";
@@ -427,7 +452,7 @@ static void
 test_dhclient_invalid_classless_routes_3 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	static const Option data[] = {
@@ -455,7 +480,7 @@ static void
 test_dhcpcd_invalid_classless_routes_3 (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	static Option data[] = {
@@ -483,7 +508,7 @@ static void
 test_dhclient_gw_in_classless_routes (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	const char *expected_gateway = "192.2.3.4";
@@ -511,7 +536,7 @@ static void
 test_dhcpcd_gw_in_classless_routes (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_route1_dest = "192.168.10.0";
 	const char *expected_route1_gw = "192.168.1.1";
 	const char *expected_gateway = "192.2.3.4";
@@ -539,7 +564,7 @@ static void
 test_escaped_domain_searches (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const char *expected_search0 = "host1";
 	const char *expected_search1 = "host2";
 	const char *expected_search2 = "host3";
@@ -566,7 +591,7 @@ static void
 test_invalid_escaped_domain_searches (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	static const Option data[] = {
 		{ "domain_search", "host1\\aahost2\\032host3" },
 		{ NULL, NULL }
@@ -591,7 +616,7 @@ static void
 test_ip4_missing_prefix (const char *ip, guint32 expected_prefix)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const NMPlatformIP4Address *address;
 
 	options = fill_table (generic_options, NULL);
@@ -631,7 +656,7 @@ static void
 test_ip4_prefix_classless (void)
 {
 	GHashTable *options;
-	NMIP4Config *ip4_config;
+	gs_unref_object NMIP4Config *ip4_config = NULL;
 	const NMPlatformIP4Address *address;
 
 	/* Ensure that the missing-subnet-mask handler doesn't mangle classless
@@ -716,6 +741,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/dhcp/ip4-missing-prefix-8", test_ip4_missing_prefix_8);
 	g_test_add_func ("/dhcp/ip4-prefix-classless", test_ip4_prefix_classless);
 	g_test_add_func ("/dhcp/client-id-from-string", test_client_id_from_string);
+	g_test_add_func ("/dhcp/vendor-option-metered", test_vendor_option_metered);
 
 	return g_test_run ();
 }
diff --git a/src/main.c b/src/main.c
index dfc307b5..495262ef 100644
--- a/src/main.c
+++ b/src/main.c
@@ -476,13 +476,7 @@ main (int argc, char *argv[])
 	                          net_enabled,
 	                          wifi_enabled,
 	                          wwan_enabled,
-	                          wimax_enabled,
-	                          &error);
-	if (manager == NULL) {
-		nm_log_err (LOGD_CORE, "failed to initialize the network manager: %s",
-		            error && error->message ? error->message : "(unknown)");
-		goto done;
-	}
+	                          wimax_enabled);
 
 	/* Initialize the supplicant manager */
 	sup_mgr = nm_supplicant_manager_get ();
diff --git a/src/nm-access-point-glue.h b/src/nm-access-point-glue.h
index ad4d5916..39b22cc2 100644
--- a/src/nm-access-point-glue.h
+++ b/src/nm-access-point-glue.h
@@ -68,6 +68,6 @@ const DBusGObjectInfo dbus_glib_nm_access_point_object_info = {  1,
   0,
 "\0",
 "org.freedesktop.NetworkManager.AccessPoint\0PropertiesChanged\0\0",
-"org.freedesktop.NetworkManager.AccessPoint\0Flags\0flags\0read\0org.freedesktop.NetworkManager.AccessPoint\0WpaFlags\0wpa_flags\0read\0org.freedesktop.NetworkManager.AccessPoint\0RsnFlags\0rsn_flags\0read\0org.freedesktop.NetworkManager.AccessPoint\0Ssid\0ssid\0read\0org.freedesktop.NetworkManager.AccessPoint\0Frequency\0frequency\0read\0org.freedesktop.NetworkManager.AccessPoint\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.AccessPoint\0Mode\0mode\0read\0org.freedesktop.NetworkManager.AccessPoint\0MaxBitrate\0max_bitrate\0read\0org.freedesktop.NetworkManager.AccessPoint\0Strength\0strength\0read\0\0"
+"org.freedesktop.NetworkManager.AccessPoint\0Flags\0flags\0read\0org.freedesktop.NetworkManager.AccessPoint\0WpaFlags\0wpa_flags\0read\0org.freedesktop.NetworkManager.AccessPoint\0RsnFlags\0rsn_flags\0read\0org.freedesktop.NetworkManager.AccessPoint\0Ssid\0ssid\0read\0org.freedesktop.NetworkManager.AccessPoint\0Frequency\0frequency\0read\0org.freedesktop.NetworkManager.AccessPoint\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.AccessPoint\0Mode\0mode\0read\0org.freedesktop.NetworkManager.AccessPoint\0MaxBitrate\0max_bitrate\0read\0org.freedesktop.NetworkManager.AccessPoint\0Strength\0strength\0read\0org.freedesktop.NetworkManager.AccessPoint\0LastSeen\0last_seen\0read\0\0"
 };
 
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index d40d571e..3a8e6cf5 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -100,6 +100,7 @@ enum {
 
 enum {
 	DEVICE_CHANGED,
+	DEVICE_METERED_CHANGED,
 	LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -109,6 +110,29 @@ static void _device_cleanup (NMActiveConnection *self);
 
 /****************************************************************/
 
+#define _NMLOG_DOMAIN         LOGD_DEVICE
+#define _NMLOG_PREFIX_NAME    "active-connection"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
+            char __prefix[128]; \
+            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
+            const void *const __self = (self); \
+            \
+            if (__self) { \
+                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
+                __p_prefix = __prefix; \
+            } \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+                     "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/****************************************************************/
+
 static const char *
 state_to_string (NMActiveConnectionState state)
 {
@@ -401,6 +425,18 @@ device_master_changed (GObject *object,
 	}
 }
 
+static void
+device_metered_changed (GObject *object,
+                        GParamSpec *pspec,
+                        gpointer user_data)
+{
+	NMActiveConnection *self = (NMActiveConnection *) user_data;
+	NMDevice *device = NM_DEVICE (object);
+
+	g_return_if_fail (NM_IS_ACTIVE_CONNECTION (self));
+	g_signal_emit (self, signals[DEVICE_METERED_CHANGED], 0, nm_device_get_metered (device));
+}
+
 gboolean
 nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
 {
@@ -427,6 +463,8 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
 		                  G_CALLBACK (device_state_changed), self);
 		g_signal_connect (device, "notify::master",
 		                  G_CALLBACK (device_master_changed), self);
+		g_signal_connect (device, "notify::" NM_DEVICE_METERED,
+		                  G_CALLBACK (device_metered_changed), self);
 
 		if (!priv->assumed) {
 			priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)self);
@@ -473,15 +511,15 @@ check_master_ready (NMActiveConnection *self)
 	NMActiveConnectionState master_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
 
 	if (priv->state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
-		nm_log_dbg (LOGD_DEVICE, "(%p): not signalling master-ready (not activating)", self);
+		_LOGD ("not signalling master-ready (not activating)");
 		return;
 	}
 	if (!priv->master) {
-		nm_log_dbg (LOGD_DEVICE, "(%p): not signalling master-ready (no master)", self);
+		_LOGD ("not signalling master-ready (no master)");
 		return;
 	}
 	if (priv->master_ready) {
-		nm_log_dbg (LOGD_DEVICE, "(%p): not signalling master-ready (already signaled)", self);
+		_LOGD ("not signalling master-ready (already signaled)");
 		return;
 	}
 
@@ -491,12 +529,12 @@ check_master_ready (NMActiveConnection *self)
 	 * or higher states.
 	 */
 	master_state = nm_active_connection_get_state (priv->master);
-	nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection [%p] state now '%s' (%d)",
-	            self, priv->master, state_to_string (master_state), master_state);
+	_LOGD ("master ActiveConnection [%p] state now '%s' (%d)",
+	       priv->master, state_to_string (master_state), master_state);
 
 	if (   master_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
 	    || master_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		nm_log_dbg (LOGD_DEVICE, "(%p): signalling master-ready", self);
+		_LOGD ("signalling master-ready");
 
 		priv->master_ready = TRUE;
 		g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_MASTER_READY);
@@ -520,8 +558,8 @@ master_state_cb (NMActiveConnection *master,
 
 	check_master_ready (self);
 
-	nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection [%p] state now '%s' (%d)",
-	            self, master, state_to_string (master_state), master_state);
+	_LOGD ("master ActiveConnection [%p] state now '%s' (%d)",
+	       master, state_to_string (master_state), master_state);
 
 	if (   master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING
 	    && !priv->master_ready) {
@@ -558,8 +596,8 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m
 		g_return_if_fail (priv->device != nm_active_connection_get_device (master));
 	}
 
-	nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection is [%p] %s",
-	            self, master, nm_active_connection_get_id (master));
+	_LOGD ("master ActiveConnection is [%p] %s",
+	       master, nm_active_connection_get_id (master));
 
 	priv->master = g_object_ref (master);
 	g_signal_connect (priv->master,
@@ -838,6 +876,7 @@ _device_cleanup (NMActiveConnection *self)
 	if (priv->device) {
 		g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_state_changed), self);
 		g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_master_changed), self);
+		g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_metered_changed), self);
 	}
 
 	if (priv->pending_activation_id) {
@@ -1043,6 +1082,14 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, NM_TYPE_DEVICE, NM_TYPE_DEVICE);
 
+	signals[DEVICE_METERED_CHANGED] =
+		g_signal_new (NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMActiveConnectionClass, device_metered_changed),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1, G_TYPE_UINT);
+
 	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
 	                                        G_TYPE_FROM_CLASS (ac_class),
 	                                        &dbus_glib_nm_active_connection_object_info);
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index 710cfeed..4db5e6ff 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -57,7 +57,8 @@
 #define NM_ACTIVE_CONNECTION_INT_MASTER_READY   "int-master-ready"
 
 /* Internal signals*/
-#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED     "device-changed"
+#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED          "device-changed"
+#define NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED  "device-metered-changed"
 
 struct _NMActiveConnection {
 	GObject parent;
@@ -78,6 +79,9 @@ typedef struct {
 	void (*device_changed) (NMActiveConnection *connection,
 	                        NMDevice *new_device,
 	                        NMDevice *old_device);
+
+	void (*device_metered_changed) (NMActiveConnection *connection,
+	                                NMMetered new_value);
 } NMActiveConnectionClass;
 
 GType         nm_active_connection_get_type (void);
diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c
index 8bd167ef..09a5791f 100644
--- a/src/nm-auth-manager.c
+++ b/src/nm-auth-manager.c
@@ -30,27 +30,21 @@
 #define POLKIT_INTERFACE                    "org.freedesktop.PolicyKit1.Authority"
 
 
-#define _LOG_DEFAULT_DOMAIN  LOGD_CORE
-
-#define _LOG(level, domain, ...) \
+#define _NMLOG_PREFIX_NAME    "auth"
+#define _NMLOG_DOMAIN         LOGD_CORE
+#define _NMLOG(level, ...) \
     G_STMT_START { \
-        if (nm_logging_enabled ((level), (domain))) { \
-            char __prefix[30] = "auth"; \
+        if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
+            char __prefix[30] = _NMLOG_PREFIX_NAME; \
             \
             if ((self) != singleton_instance) \
-                g_snprintf (__prefix, sizeof (__prefix), "auth[%p]", (self)); \
-            _nm_log ((level), (domain), 0, \
+                g_snprintf (__prefix, sizeof (__prefix), ""_NMLOG_PREFIX_NAME"[%p]", (self)); \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
     } G_STMT_END
 
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO,  _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN,  _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR,   _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-
-
 enum {
 	PROP_0,
 	PROP_POLKIT_ENABLED,
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 81ac8f9d..e7e52cae 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -36,24 +36,15 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
 
 #define NM_CONNECTIVITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTIVITY, NMConnectivityPrivate))
 
-
-#define _LOG_DEFAULT_DOMAIN  LOGD_CONCHECK
-
-#define _LOG(level, domain, ...) \
+#define _NMLOG_DOMAIN  LOGD_CONCHECK
+#define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (domain), \
+        nm_log ((level), (_NMLOG_DOMAIN), \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 "connectivity: " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
     } G_STMT_END
 
-#define _LOGT(...)      _LOG (LOGL_TRACE, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO,  _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN,  _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR,   _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-
-
 typedef struct {
 	char *uri;
 	char *response;
@@ -151,25 +142,31 @@ nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_
 		goto done;
 	}
 
-	/* Check headers; if we find the NM-specific one we're done */
-	nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status");
-	if (g_strcmp0 (nm_header, "online") == 0) {
-		_LOGD ("check for uri '%s' with Status header successful.", uri);
-		new_state = NM_CONNECTIVITY_FULL;
-	} else if (msg->status_code == SOUP_STATUS_OK) {
-		/* check response */
-		if (msg->response_body->data && g_str_has_prefix (msg->response_body->data, response)) {
-			_LOGD ("check for uri '%s' successful.", uri);
+	if (msg->status_code == 511) {
+		_LOGD ("check for uri '%s' returned status '%d %s'; captive portal present.",
+			   uri, msg->status_code, msg->reason_phrase);
+		new_state = NM_CONNECTIVITY_PORTAL;
+	} else {
+		/* Check headers; if we find the NM-specific one we're done */
+		nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status");
+		if (g_strcmp0 (nm_header, "online") == 0) {
+			_LOGD ("check for uri '%s' with Status header successful.", uri);
 			new_state = NM_CONNECTIVITY_FULL;
+		} else if (msg->status_code == SOUP_STATUS_OK) {
+			/* check response */
+			if (msg->response_body->data && g_str_has_prefix (msg->response_body->data, response)) {
+				_LOGD ("check for uri '%s' successful.", uri);
+				new_state = NM_CONNECTIVITY_FULL;
+			} else {
+				_LOGI ("check for uri '%s' did not match expected response '%s'; assuming captive portal.",
+					   uri, response);
+				new_state = NM_CONNECTIVITY_PORTAL;
+			}
 		} else {
-			_LOGI ("check for uri '%s' did not match expected response '%s'; assuming captive portal.",
-			       uri, response);
+			_LOGI ("check for uri '%s' returned status '%d %s'; assuming captive portal.",
+				   uri, msg->status_code, msg->reason_phrase);
 			new_state = NM_CONNECTIVITY_PORTAL;
 		}
-	} else {
-		_LOGI ("check for uri '%s' returned status '%d %s'; assuming captive portal.",
-		       uri, msg->status_code, msg->reason_phrase);
-		new_state = NM_CONNECTIVITY_PORTAL;
 	}
 
  done:
@@ -365,12 +362,14 @@ set_property (GObject *object, guint property_id,
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 	const char *uri, *response;
 	guint interval;
+	gboolean changed;
 
 	switch (property_id) {
 	case PROP_URI:
 		uri = g_value_get_string (value);
 		if (uri && !*uri)
 			uri = NULL;
+		changed = g_strcmp0 (uri, priv->uri) != 0;
 #if WITH_CONCHECK
 		if (uri) {
 			SoupURI *soup_uri = soup_uri_new (uri);
@@ -379,11 +378,14 @@ set_property (GObject *object, guint property_id,
 				_LOGE ("invalid uri '%s' for connectivity check.", uri);
 				uri = NULL;
 			}
+			if (uri && soup_uri && changed &&
+			    soup_uri_get_scheme(soup_uri) == SOUP_URI_SCHEME_HTTPS)
+				_LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri);
 			if (soup_uri)
 				soup_uri_free (soup_uri);
 		}
 #endif
-		if (g_strcmp0 (uri, priv->uri) != 0) {
+		if (changed) {
 			g_free (priv->uri);
 			priv->uri = g_strdup (uri);
 			_reschedule_periodic_checks (self, TRUE);
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c
index fbb07ce3..96a8c855 100644
--- a/src/nm-default-route-manager.c
+++ b/src/nm-default-route-manager.c
@@ -21,10 +21,9 @@
 
 #include "config.h"
 
-#include "nm-default-route-manager.h"
-
-#include "string.h"
+#include <string.h>
 
+#include "nm-default-route-manager.h"
 #include "nm-logging.h"
 #include "nm-device.h"
 #include "nm-vpn-connection.h"
@@ -62,7 +61,17 @@ G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT)
 
 NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, NM_TYPE_DEFAULT_ROUTE_MANAGER);
 
-#define _LOG(level, addr_family, ...) \
+#define _NMLOG_PREFIX_NAME   "default-route"
+#undef  _NMLOG_ENABLED
+#define _NMLOG_ENABLED(level, addr_family) \
+    ({ \
+        const int __addr_family = (addr_family); \
+        const NMLogLevel __level = (level); \
+        const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \
+        \
+        nm_logging_enabled (__level, __domain); \
+    })
+#define _NMLOG(level, addr_family, ...) \
     G_STMT_START { \
         const int __addr_family = (addr_family); \
         const NMLogLevel __level = (level); \
@@ -70,23 +79,18 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get,
         \
         if (nm_logging_enabled (__level, __domain)) { \
             char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \
-            char __prefix[30] = "default-route"; \
+            char __prefix[30] = _NMLOG_PREFIX_NAME; \
             \
             if ((self) != singleton_instance) \
-                g_snprintf (__prefix, sizeof (__prefix), "default-route%c[%p]", __ch, (self)); \
+                g_snprintf (__prefix, sizeof (__prefix), ""_NMLOG_PREFIX_NAME"%c[%p]", __ch, (self)); \
             else \
-                __prefix[STRLEN ("default-route")] = __ch; \
+                __prefix[STRLEN (_NMLOG_PREFIX_NAME)] = __ch; \
             _nm_log (__level, __domain, 0, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
     } G_STMT_END
 
-#define _LOGD(addr_family, ...)      _LOG (LOGL_DEBUG, addr_family, __VA_ARGS__)
-#define _LOGI(addr_family, ...)      _LOG (LOGL_INFO , addr_family, __VA_ARGS__)
-#define _LOGW(addr_family, ...)      _LOG (LOGL_WARN , addr_family, __VA_ARGS__)
-#define _LOGE(addr_family, ...)      _LOG (LOGL_ERR  , addr_family, __VA_ARGS__)
-
 #define LOG_ENTRY_FMT  "entry[%u/%s:%p:%s:%c:%csync]"
 #define LOG_ENTRY_ARGS(entry_idx, entry) \
 		(entry_idx), \
@@ -1290,7 +1294,9 @@ _resync_idle_reschedule (NMDefaultRouteManager *self)
 			g_source_remove (priv->resync.idle_handle);
 		else
 			_LOGD (0, "resync: schedule on idle");
-		priv->resync.idle_handle = g_idle_add ((GSourceFunc) _resync_idle_now, self);
+		/* Schedule this at low priority so that on an external change to platform
+		 * a NMDevice has a chance to picks up the changes first. */
+		priv->resync.idle_handle = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) _resync_idle_now, self, NULL);
 	} else if (!priv->resync.idle_handle) {
 		priv->resync.idle_handle =  g_timeout_add (priv->resync.backoff_wait_time_ms, (GSourceFunc) _resync_idle_now, self);
 		_LOGD (0, "resync: schedule in %u.%03u seconds (%u)", priv->resync.backoff_wait_time_ms/1000,
diff --git a/src/nm-device-glue.h b/src/nm-device-glue.h
index a740b7a7..c511c84c 100644
--- a/src/nm-device-glue.h
+++ b/src/nm-device-glue.h
@@ -74,6 +74,6 @@ const DBusGObjectInfo dbus_glib_nm_device_object_info = {  1,
   2,
 "org.freedesktop.NetworkManager.Device\0Disconnect\0A\0\0org.freedesktop.NetworkManager.Device\0Delete\0A\0\0\0",
 "org.freedesktop.NetworkManager.Device\0StateChanged\0\0",
-"org.freedesktop.NetworkManager.Device\0Udi\0udi\0read\0org.freedesktop.NetworkManager.Device\0Interface\0interface\0read\0org.freedesktop.NetworkManager.Device\0IpInterface\0ip_interface\0read\0org.freedesktop.NetworkManager.Device\0Driver\0driver\0read\0org.freedesktop.NetworkManager.Device\0DriverVersion\0driver_version\0read\0org.freedesktop.NetworkManager.Device\0FirmwareVersion\0firmware_version\0read\0org.freedesktop.NetworkManager.Device\0Capabilities\0capabilities\0read\0org.freedesktop.NetworkManager.Device\0Ip4Address\0ip4_address\0read\0org.freedesktop.NetworkManager.Device\0State\0state\0read\0org.freedesktop.NetworkManager.Device\0StateReason\0state_reason\0read\0org.freedesktop.NetworkManager.Device\0ActiveConnection\0active_connection\0read\0org.freedesktop.NetworkManager.Device\0Ip4Config\0ip4_config\0read\0org.freedesktop.NetworkManager.Device\0Dhcp4Config\0dhcp4_config\0read\0org.freedesktop.NetworkManager.Device\0Ip6Config\0ip6_config\0read\0org.freedesktop.NetworkManager.Device\0Dhcp6Config\0dhcp6_config\0read\0org.freedesktop.NetworkManager.Device\0Managed\0managed\0read\0org.freedesktop.NetworkManager.Device\0Autoconnect\0autoconnect\0readwrite\0org.freedesktop.NetworkManager.Device\0FirmwareMissing\0firmware_missing\0read\0org.freedesktop.NetworkManager.Device\0DeviceType\0device_type\0read\0org.freedesktop.NetworkManager.Device\0AvailableConnections\0available_connections\0read\0org.freedesktop.NetworkManager.Device\0PhysicalPortId\0physical_port_id\0read\0org.freedesktop.NetworkManager.Device\0Mtu\0mtu\0read\0\0"
+"org.freedesktop.NetworkManager.Device\0Udi\0udi\0read\0org.freedesktop.NetworkManager.Device\0Interface\0interface\0read\0org.freedesktop.NetworkManager.Device\0IpInterface\0ip_interface\0read\0org.freedesktop.NetworkManager.Device\0Driver\0driver\0read\0org.freedesktop.NetworkManager.Device\0DriverVersion\0driver_version\0read\0org.freedesktop.NetworkManager.Device\0FirmwareVersion\0firmware_version\0read\0org.freedesktop.NetworkManager.Device\0Capabilities\0capabilities\0read\0org.freedesktop.NetworkManager.Device\0Ip4Address\0ip4_address\0read\0org.freedesktop.NetworkManager.Device\0State\0state\0read\0org.freedesktop.NetworkManager.Device\0StateReason\0state_reason\0read\0org.freedesktop.NetworkManager.Device\0ActiveConnection\0active_connection\0read\0org.freedesktop.NetworkManager.Device\0Ip4Config\0ip4_config\0read\0org.freedesktop.NetworkManager.Device\0Dhcp4Config\0dhcp4_config\0read\0org.freedesktop.NetworkManager.Device\0Ip6Config\0ip6_config\0read\0org.freedesktop.NetworkManager.Device\0Dhcp6Config\0dhcp6_config\0read\0org.freedesktop.NetworkManager.Device\0Managed\0managed\0read\0org.freedesktop.NetworkManager.Device\0Autoconnect\0autoconnect\0readwrite\0org.freedesktop.NetworkManager.Device\0FirmwareMissing\0firmware_missing\0read\0org.freedesktop.NetworkManager.Device\0DeviceType\0device_type\0read\0org.freedesktop.NetworkManager.Device\0AvailableConnections\0available_connections\0read\0org.freedesktop.NetworkManager.Device\0PhysicalPortId\0physical_port_id\0read\0org.freedesktop.NetworkManager.Device\0Mtu\0mtu\0read\0org.freedesktop.NetworkManager.Device\0Metered\0metered\0read\0\0"
 };
 
diff --git a/src/nm-enum-types.c b/src/nm-enum-types.c
index f92158d3..1ff95970 100644
--- a/src/nm-enum-types.c
+++ b/src/nm-enum-types.c
@@ -741,6 +741,26 @@ nm_pobject_type_get_type (void)
   return g_define_type_id__volatile;
 }
 GType
+nm_ip_config_merge_flags_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GFlagsValue values[] = {
+        { NM_IP_CONFIG_MERGE_DEFAULT, "NM_IP_CONFIG_MERGE_DEFAULT", "default" },
+        { NM_IP_CONFIG_MERGE_NO_ROUTES, "NM_IP_CONFIG_MERGE_NO_ROUTES", "no-routes" },
+        { NM_IP_CONFIG_MERGE_NO_DNS, "NM_IP_CONFIG_MERGE_NO_DNS", "no-dns" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_flags_register_static (g_intern_static_string ("NMIPConfigMergeFlags"), 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 86f17f04..0997211e 100644
--- a/src/nm-enum-types.h
+++ b/src/nm-enum-types.h
@@ -64,6 +64,8 @@ GType nm_link_type_get_type (void) G_GNUC_CONST;
 #define NM_TYPE_LINK_TYPE (nm_link_type_get_type ())
 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_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 2192f36b..18966c57 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -87,6 +87,7 @@ dhcp4_state_changed (NMDhcpClient *client,
                      NMDhcpState state,
                      NMIP4Config *ip4_config,
                      GHashTable *options,
+                     const char *event_id,
                      gpointer user_data)
 {
 	static NMIP4Config *last_config = NULL;
@@ -103,7 +104,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 		if (last_config)
 			nm_ip4_config_subtract (existing, last_config);
 
-		nm_ip4_config_merge (existing, ip4_config);
+		nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 		if (!nm_ip4_config_commit (existing, ifindex, TRUE, global_opt.priority_v4))
 			nm_log_warn (LOGD_DHCP4, "(%s): failed to apply DHCPv4 config", global_opt.ifname);
 
@@ -240,7 +241,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da
 	if (last_config)
 		nm_ip6_config_subtract (existing, last_config);
 
-	nm_ip6_config_merge (existing, ip6_config);
+	nm_ip6_config_merge (existing, ip6_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);
 
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index db8c4b43..5918f3dc 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -35,6 +35,7 @@
 #include "nm-core-internal.h"
 #include "nm-route-manager.h"
 #include "gsystem-local-alloc.h"
+#include "nm-macros-internal.h"
 
 G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT)
 
@@ -45,6 +46,7 @@ typedef struct {
 
 	gboolean never_default;
 	guint32 gateway;
+	gboolean has_gateway;
 	GArray *addresses;
 	GArray *routes;
 	GArray *nameservers;
@@ -56,6 +58,8 @@ typedef struct {
 	GArray *wins;
 	guint32 mtu;
 	NMIPConfigSource mtu_source;
+	gint64 route_metric;
+	gboolean metered;
 } NMIP4ConfigPrivate;
 
 /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */
@@ -185,7 +189,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	guint i;
 	guint32 lowest_metric = G_MAXUINT32;
 	guint32 old_gateway = 0;
-	gboolean has_gateway = FALSE;
+	gboolean old_has_gateway = FALSE;
 
 	/* Slaves have no IP configuration */
 	if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
@@ -202,6 +206,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 
 	/* Extract gateway from default route */
 	old_gateway = priv->gateway;
+	old_has_gateway = priv->has_gateway;
 	for (i = 0; i < priv->routes->len; ) {
 		const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
 
@@ -210,7 +215,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 				priv->gateway = route->gateway;
 				lowest_metric = route->metric;
 			}
-			has_gateway = TRUE;
+			priv->has_gateway = TRUE;
 			/* Remove the default route from the list */
 			g_array_remove_index_fast (priv->routes, i);
 			continue;
@@ -218,10 +223,14 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 		i++;
 	}
 
+	/* we detect the route metric based on the default route. All non-default
+	 * routes have their route metrics explicitly set. */
+	priv->route_metric = priv->has_gateway ? (gint64) lowest_metric : (gint64) -1;
+
 	/* If there is a host route to the gateway, ignore that route.  It is
 	 * automatically added by NetworkManager when needed.
 	 */
-	if (has_gateway) {
+	if (priv->has_gateway) {
 		for (i = 0; i < priv->routes->len; i++) {
 			const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
 
@@ -237,7 +246,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	/* If the interface has the default route, and has IPv4 addresses, capture
 	 * nameservers from /etc/resolv.conf.
 	 */
-	if (priv->addresses->len && has_gateway && capture_resolv_conf) {
+	if (priv->addresses->len && priv->has_gateway && capture_resolv_conf) {
 		if (nm_ip4_config_capture_resolv_conf (priv->nameservers, NULL))
 			_NOTIFY (config, PROP_NAMESERVERS);
 	}
@@ -247,7 +256,8 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	_NOTIFY (config, PROP_ROUTE_DATA);
 	_NOTIFY (config, PROP_ADDRESSES);
 	_NOTIFY (config, PROP_ROUTES);
-	if (priv->gateway != old_gateway)
+	if (   priv->gateway != old_gateway
+	    || priv->has_gateway != old_has_gateway)
 		_NOTIFY (config, PROP_GATEWAY);
 
 	return config;
@@ -335,6 +345,7 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 void
 nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
 {
+	NMIP4ConfigPrivate *priv;
 	guint naddresses, nroutes, nnameservers, nsearches;
 	int i;
 
@@ -343,6 +354,8 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 
 	g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
 
+	priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
 	g_object_freeze_notify (G_OBJECT (config));
 
 	naddresses = nm_setting_ip_config_get_num_addresses (setting);
@@ -362,6 +375,9 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 		nm_ip4_config_set_gateway (config, gateway);
 	}
 
+	if (priv->route_metric  == -1)
+		priv->route_metric = nm_setting_ip_config_get_route_metric (setting);
+
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
 		NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
@@ -430,6 +446,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	guint naddresses, nroutes, nnameservers, nsearches;
 	const char *method = NULL;
 	int i;
+	gint64 route_metric;
 
 	s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
 
@@ -445,6 +462,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	nroutes = nm_ip4_config_get_num_routes (config);
 	nnameservers = nm_ip4_config_get_num_nameservers (config);
 	nsearches = nm_ip4_config_get_num_searches (config);
+	route_metric = nm_ip4_config_get_route_metric (config);
 
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
@@ -470,7 +488,7 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	}
 
 	/* Gateway */
-	if (   gateway
+	if (   nm_ip4_config_has_gateway (config)
 	    && nm_setting_ip_config_get_num_addresses (s_ip4) > 0) {
 		g_object_set (s_ip4,
 		              NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet4_ntop (gateway, NULL),
@@ -480,7 +498,11 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 	/* Use 'disabled' if the method wasn't previously set */
 	if (!method)
 		method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
-	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, method, NULL);
+
+	g_object_set (s_ip4,
+	              NM_SETTING_IP_CONFIG_METHOD, method,
+	              NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric,
+	              NULL);
 
 	/* Routes */
 	for (i = 0; i < nroutes; i++) {
@@ -521,13 +543,17 @@ nm_ip4_config_create_setting (const NMIP4Config *config)
 /******************************************************************/
 
 void
-nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
+nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags)
 {
+	NMIP4ConfigPrivate *dst_priv, *src_priv;
 	guint32 i;
 
 	g_return_if_fail (src != NULL);
 	g_return_if_fail (dst != NULL);
 
+	dst_priv = NM_IP4_CONFIG_GET_PRIVATE (dst);
+	src_priv = NM_IP4_CONFIG_GET_PRIVATE (src);
+
 	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
@@ -535,24 +561,37 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
 		nm_ip4_config_add_address (dst, nm_ip4_config_get_address (src, i));
 
 	/* nameservers */
-	for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++)
-		nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++)
+			nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i));
+	}
 
 	/* default gateway */
-	if (nm_ip4_config_get_gateway (src))
+	if (nm_ip4_config_has_gateway (src))
 		nm_ip4_config_set_gateway (dst, nm_ip4_config_get_gateway (src));
 
 	/* routes */
-	for (i = 0; i < nm_ip4_config_get_num_routes (src); i++)
-		nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
+		for (i = 0; i < nm_ip4_config_get_num_routes (src); i++)
+			nm_ip4_config_add_route (dst, nm_ip4_config_get_route (src, i));
+	}
+
+	if (dst_priv->route_metric == -1)
+		dst_priv->route_metric = src_priv->route_metric;
+	else if (src_priv->route_metric != -1)
+		dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric);
 
 	/* domains */
-	for (i = 0; i < nm_ip4_config_get_num_domains (src); i++)
-		nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_domains (src); i++)
+			nm_ip4_config_add_domain (dst, nm_ip4_config_get_domain (src, i));
+	}
 
 	/* dns searches */
-	for (i = 0; i < nm_ip4_config_get_num_searches (src); i++)
-		nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_searches (src); i++)
+			nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i));
+	}
 
 	/* MSS */
 	if (nm_ip4_config_get_mss (src))
@@ -564,15 +603,23 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src)
 		                       nm_ip4_config_get_mtu_source (src));
 
 	/* NIS */
-	for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++)
-		nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++)
+			nm_ip4_config_add_nis_server (dst, nm_ip4_config_get_nis_server (src, i));
 
-	if (nm_ip4_config_get_nis_domain (src))
-		nm_ip4_config_set_nis_domain (dst, nm_ip4_config_get_nis_domain (src));
+		if (nm_ip4_config_get_nis_domain (src))
+			nm_ip4_config_set_nis_domain (dst, nm_ip4_config_get_nis_domain (src));
+	}
 
 	/* WINS */
-	for (i = 0; i < nm_ip4_config_get_num_wins (src); i++)
-		nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip4_config_get_num_wins (src); i++)
+			nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i));
+	}
+
+	/* metered flag */
+	nm_ip4_config_set_metered (dst, nm_ip4_config_get_metered (dst) ||
+	                                nm_ip4_config_get_metered (src));
 
 	g_object_thaw_notify (G_OBJECT (dst));
 }
@@ -721,11 +768,14 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src)
 	}
 
 	/* default gateway */
-	if (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst))
-		nm_ip4_config_set_gateway (dst, 0);
+	if (   (nm_ip4_config_has_gateway (src) == nm_ip4_config_has_gateway (dst))
+	    && (nm_ip4_config_get_gateway (src) == nm_ip4_config_get_gateway (dst)))
+		nm_ip4_config_unset_gateway (dst);
 
 	if (!nm_ip4_config_get_num_addresses (dst))
-		nm_ip4_config_set_gateway (dst, 0);
+		nm_ip4_config_unset_gateway (dst);
+
+	/* ignore route_metric */
 
 	/* routes */
 	for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) {
@@ -796,12 +846,15 @@ nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src)
 			i++;
 	}
 
+	/* ignore route_metric */
 	/* ignore nameservers */
 
 	/* default gateway */
 	if (   !nm_ip4_config_get_num_addresses (dst)
-	    || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst)))
-		nm_ip4_config_set_gateway (dst, 0);
+	    || (nm_ip4_config_has_gateway (src) != nm_ip4_config_has_gateway (dst))
+	    || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst))) {
+		nm_ip4_config_unset_gateway (dst);
+	}
 
 	/* routes */
 	for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) {
@@ -867,11 +920,17 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 	}
 
 	/* default gateway */
-	if (src_priv->gateway != dst_priv->gateway) {
+	if (   src_priv->gateway != dst_priv->gateway
+	    || src_priv->has_gateway != dst_priv->has_gateway) {
 		nm_ip4_config_set_gateway (dst, src_priv->gateway);
 		has_relevant_changes = TRUE;
 	}
 
+	if (src_priv->route_metric != dst_priv->route_metric) {
+		dst_priv->route_metric = src_priv->route_metric;
+		has_minor_changes = TRUE;
+	}
+
 	/* addresses */
 	num = nm_ip4_config_get_num_addresses (src);
 	are_equal = num == nm_ip4_config_get_num_addresses (dst);
@@ -1028,6 +1087,12 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev
 		has_minor_changes = TRUE;
 	}
 
+	/* metered */
+	if (src_priv->metered != dst_priv->metered) {
+		dst_priv->metered = src_priv->metered;
+		has_minor_changes = TRUE;
+	}
+
 	/* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes
 	 * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */
 	g_assert (config_equal == !has_relevant_changes);
@@ -1059,8 +1124,10 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 		g_message ("      a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i)));
 
 	/* default gateway */
-	tmp = nm_ip4_config_get_gateway (config);
-	g_message ("     gw: %s", nm_utils_inet4_ntop (tmp, NULL));
+	if (nm_ip4_config_has_gateway (config)) {
+		tmp = nm_ip4_config_get_gateway (config);
+		g_message ("     gw: %s", nm_utils_inet4_ntop (tmp, NULL));
+	}
 
 	/* nameservers */
 	for (i = 0; i < nm_ip4_config_get_num_nameservers (config); i++) {
@@ -1098,6 +1165,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail)
 	}
 
 	g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config));
+	g_message (" mtrd:   %d", (int) nm_ip4_config_get_metered (config));
 }
 
 gboolean
@@ -1139,12 +1207,33 @@ nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway)
 {
 	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 
-	if (priv->gateway != gateway) {
+	if (priv->gateway != gateway || !priv->has_gateway) {
 		priv->gateway = gateway;
+		priv->has_gateway = TRUE;
 		_NOTIFY (config, PROP_GATEWAY);
 	}
 }
 
+void
+nm_ip4_config_unset_gateway (NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	if (priv->has_gateway) {
+		priv->gateway = 0;
+		priv->has_gateway = FALSE;
+		_NOTIFY (config, PROP_GATEWAY);
+	}
+}
+
+gboolean
+nm_ip4_config_has_gateway (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->has_gateway;
+}
+
 guint32
 nm_ip4_config_get_gateway (const NMIP4Config *config)
 {
@@ -1153,6 +1242,14 @@ nm_ip4_config_get_gateway (const NMIP4Config *config)
 	return priv->gateway;
 }
 
+gint64
+nm_ip4_config_get_route_metric (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->route_metric;
+}
+
 /******************************************************************/
 
 void
@@ -1744,6 +1841,24 @@ nm_ip4_config_get_mtu_source (const NMIP4Config *config)
 
 /******************************************************************/
 
+void
+nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	priv->metered = !!metered;
+}
+
+gboolean
+nm_ip4_config_get_metered (const NMIP4Config *config)
+{
+	NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+
+	return priv->metered;
+}
+
+/******************************************************************/
+
 static inline void
 hash_u32 (GChecksum *sum, guint32 n)
 {
@@ -1760,6 +1875,7 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only
 	g_return_if_fail (sum);
 
 	if (!dns_only) {
+		hash_u32 (sum, nm_ip4_config_has_gateway (config));
 		hash_u32 (sum, nm_ip4_config_get_gateway (config));
 
 		for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) {
@@ -1856,6 +1972,7 @@ nm_ip4_config_init (NMIP4Config *config)
 	priv->searches = g_ptr_array_new_with_free_func (g_free);
 	priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32));
 	priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32));
+	priv->route_metric = -1;
 }
 
 static void
@@ -2020,7 +2137,7 @@ get_property (GObject *object, guint prop_id,
 		}
 		break;
 	case PROP_GATEWAY:
-		if (priv->gateway)
+		if (priv->has_gateway)
 			g_value_set_string (value, nm_utils_inet4_ntop (priv->gateway, NULL));
 		else
 			g_value_set_string (value, NULL);
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index 46367524..cb3038aa 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -69,7 +69,7 @@ void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *settin
 NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config);
 
 /* Utility functions */
-void nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src);
+void nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src, NMIPConfigMergeFlags merge_flags);
 void nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src);
 void nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src);
 gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes);
@@ -80,7 +80,10 @@ void nm_ip4_config_dump (const NMIP4Config *config, const char *detail);
 void nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default);
 gboolean nm_ip4_config_get_never_default (const NMIP4Config *config);
 void nm_ip4_config_set_gateway (NMIP4Config *config, guint32 gateway);
+void nm_ip4_config_unset_gateway (NMIP4Config *config);
+gboolean nm_ip4_config_has_gateway (const NMIP4Config *config);
 guint32 nm_ip4_config_get_gateway (const NMIP4Config *config);
+gint64 nm_ip4_config_get_route_metric (const NMIP4Config *config);
 
 /* Addresses */
 void nm_ip4_config_reset_addresses (NMIP4Config *config);
@@ -146,6 +149,10 @@ void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource s
 guint32 nm_ip4_config_get_mtu (const NMIP4Config *config);
 NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *config);
 
+/* Metered */
+void nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered);
+gboolean nm_ip4_config_get_metered (const NMIP4Config *config);
+
 void nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only);
 gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b);
 
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 48aeb64a..9647268d 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -34,6 +34,7 @@
 #include "nm-ip6-config-glue.h"
 #include "nm-route-manager.h"
 #include "NetworkManagerUtils.h"
+#include "nm-macros-internal.h"
 
 G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, G_TYPE_OBJECT)
 
@@ -50,6 +51,7 @@ typedef struct {
 	GPtrArray *domains;
 	GPtrArray *searches;
 	guint32 mss;
+	gint64 route_metric;
 } NMIP6ConfigPrivate;
 
 
@@ -329,6 +331,10 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 		i++;
 	}
 
+	/* we detect the route metric based on the default route. All non-default
+	 * routes have their route metrics explicitly set. */
+	priv->route_metric = has_gateway ? (gint64) lowest_metric : (gint64) -1;
+
 	/* If there is a host route to the gateway, ignore that route.  It is
 	 * automatically added by NetworkManager when needed.
 	 */
@@ -408,6 +414,7 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_fu
 void
 nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
 {
+	NMIP6ConfigPrivate *priv;
 	guint naddresses, nroutes, nnameservers, nsearches;
 	const char *gateway_str;
 	int i;
@@ -417,6 +424,8 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu
 
 	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
 
+	priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
 	naddresses = nm_setting_ip_config_get_num_addresses (setting);
 	nroutes = nm_setting_ip_config_get_num_routes (setting);
 	nnameservers = nm_setting_ip_config_get_num_dns (setting);
@@ -437,6 +446,9 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu
 		nm_ip6_config_set_gateway (config, &gateway);
 	}
 
+	if (priv->route_metric  == -1)
+		priv->route_metric = nm_setting_ip_config_get_route_metric (setting);
+
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
 		NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
@@ -500,6 +512,7 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 	guint naddresses, nroutes, nnameservers, nsearches;
 	const char *method = NULL;
 	int i;
+	gint64 route_metric;
 
 	s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
 
@@ -515,6 +528,7 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 	nroutes = nm_ip6_config_get_num_routes (config);
 	nnameservers = nm_ip6_config_get_num_nameservers (config);
 	nsearches = nm_ip6_config_get_num_searches (config);
+	route_metric = nm_ip6_config_get_route_metric (config);
 
 	/* Addresses */
 	for (i = 0; i < naddresses; i++) {
@@ -554,7 +568,11 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 	/* Use 'ignore' if the method wasn't previously set */
 	if (!method)
 		method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
-	g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, NULL);
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP_CONFIG_METHOD, method,
+	              NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric,
+	              NULL);
 
 	/* Routes */
 	for (i = 0; i < nroutes; i++) {
@@ -599,13 +617,17 @@ nm_ip6_config_create_setting (const NMIP6Config *config)
 /******************************************************************/
 
 void
-nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src)
+nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags)
 {
+	NMIP6ConfigPrivate *dst_priv, *src_priv;
 	guint32 i;
 
 	g_return_if_fail (src != NULL);
 	g_return_if_fail (dst != NULL);
 
+	dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
+	src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
+
 	g_object_freeze_notify (G_OBJECT (dst));
 
 	/* addresses */
@@ -621,16 +643,27 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src)
 		nm_ip6_config_set_gateway (dst, nm_ip6_config_get_gateway (src));
 
 	/* routes */
-	for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
-		nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
+		for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
+			nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
+	}
+
+	if (dst_priv->route_metric == -1)
+		dst_priv->route_metric = src_priv->route_metric;
+	else if (src_priv->route_metric != -1)
+		dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric);
 
 	/* domains */
-	for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
-		nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
+			nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
+	}
 
 	/* dns searches */
-	for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
-		nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
+	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
+		for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
+			nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
+	}
 
 	if (nm_ip6_config_get_mss (src))
 		nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
@@ -776,6 +809,8 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src)
 	if (!nm_ip6_config_get_num_addresses (dst))
 		nm_ip6_config_set_gateway (dst, NULL);
 
+	/* ignore route_metric */
+
 	/* routes */
 	for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) {
 		idx = _routes_get_index (dst, nm_ip6_config_get_route (src, i));
@@ -824,6 +859,7 @@ nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src)
 			i++;
 	}
 
+	/* ignore route_metric */
 	/* ignore nameservers */
 
 	/* default gateway */
@@ -902,6 +938,11 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev
 		has_relevant_changes = TRUE;
 	}
 
+	if (src_priv->route_metric != dst_priv->route_metric) {
+		dst_priv->route_metric = src_priv->route_metric;
+		has_minor_changes = TRUE;
+	}
+
 	/* addresses */
 	num = nm_ip6_config_get_num_addresses (src);
 	are_equal = num == nm_ip6_config_get_num_addresses (dst);
@@ -1112,6 +1153,14 @@ nm_ip6_config_get_gateway (const NMIP6Config *config)
 	return IN6_IS_ADDR_UNSPECIFIED (&priv->gateway) ? NULL : &priv->gateway;
 }
 
+gint64
+nm_ip6_config_get_route_metric (const NMIP6Config *config)
+{
+	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+	return priv->route_metric;
+}
+
 /******************************************************************/
 
 void
@@ -1672,6 +1721,7 @@ nm_ip6_config_init (NMIP6Config *config)
 	priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr));
 	priv->domains = g_ptr_array_new_with_free_func (g_free);
 	priv->searches = g_ptr_array_new_with_free_func (g_free);
+	priv->route_metric = -1;
 }
 
 static void
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index 66f15888..279f89a7 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -69,7 +69,7 @@ void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *settin
 NMSetting *nm_ip6_config_create_setting (const NMIP6Config *config);
 
 /* Utility functions */
-void nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src);
+void nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags);
 void nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src);
 void nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src);
 gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes);
@@ -81,6 +81,7 @@ void nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_defaul
 gboolean nm_ip6_config_get_never_default (const NMIP6Config *config);
 void nm_ip6_config_set_gateway (NMIP6Config *config, const struct in6_addr *);
 const struct in6_addr *nm_ip6_config_get_gateway (const NMIP6Config *config);
+gint64 nm_ip6_config_get_route_metric (const NMIP6Config *config);
 
 /* Addresses */
 void nm_ip6_config_reset_addresses (NMIP6Config *config);
diff --git a/src/nm-logging.h b/src/nm-logging.h
index 82a4e71f..40218062 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -167,4 +167,35 @@ gboolean nm_logging_setup (const char  *level,
 void     nm_logging_syslog_openlog   (gboolean debug);
 void     nm_logging_syslog_closelog  (void);
 
+/*****************************************************************************/
+
+/* This is the default definition of _NMLOG_ENABLED(). Special implementations
+ * might want to undef this and redefine it. */
+#define _NMLOG_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG_DOMAIN)) )
+
+#define _LOGt(...)          _NMLOG (LOGL_TRACE, __VA_ARGS__)
+#define _LOGD(...)          _NMLOG (LOGL_DEBUG, __VA_ARGS__)
+#define _LOGI(...)          _NMLOG (LOGL_INFO , __VA_ARGS__)
+#define _LOGW(...)          _NMLOG (LOGL_WARN , __VA_ARGS__)
+#define _LOGE(...)          _NMLOG (LOGL_ERR  , __VA_ARGS__)
+
+#define _LOGt_ENABLED(...)  _NMLOG_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
+#define _LOGD_ENABLED(...)  _NMLOG_ENABLED (LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOGI_ENABLED(...)  _NMLOG_ENABLED (LOGL_INFO , ##__VA_ARGS__)
+#define _LOGW_ENABLED(...)  _NMLOG_ENABLED (LOGL_WARN , ##__VA_ARGS__)
+#define _LOGE_ENABLED(...)  _NMLOG_ENABLED (LOGL_ERR  , ##__VA_ARGS__)
+
+/* _LOGt() and _LOGT() both log with level TRACE, but the latter is disabled by default,
+ * unless building with --with-more-logging. */
+#ifdef NM_MORE_LOGGING
+#define _LOGT_ENABLED(...)  _NMLOG_ENABLED (LOGL_TRACE, ##__VA_ARGS__)
+#define _LOGT(...)          _NMLOG (LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimize out. */
+#define _LOGT_ENABLED(...)  ( FALSE && (_NMLOG_ENABLED (LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOGT(...)          G_STMT_START { if (FALSE) { _NMLOG (LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+/*****************************************************************************/
+
 #endif /* __NETWORKMANAGER_LOGGING_H__ */
diff --git a/src/nm-manager-glue.h b/src/nm-manager-glue.h
index d907f4d2..f7840c9f 100644
--- a/src/nm-manager-glue.h
+++ b/src/nm-manager-glue.h
@@ -411,6 +411,6 @@ const DBusGObjectInfo dbus_glib_nm_manager_object_info = {  1,
   12,
 "org.freedesktop.NetworkManager\0GetDevices\0S\0devices\0O\0F\0N\0ao\0\0org.freedesktop.NetworkManager\0GetDeviceByIpIface\0S\0iface\0I\0s\0device\0O\0F\0N\0o\0\0org.freedesktop.NetworkManager\0ActivateConnection\0A\0connection\0I\0o\0device\0I\0o\0specific_object\0I\0o\0active_connection\0O\0F\0N\0o\0\0org.freedesktop.NetworkManager\0AddAndActivateConnection\0A\0connection\0I\0a{sa{sv}}\0device\0I\0o\0specific_object\0I\0o\0path\0O\0F\0N\0o\0active_connection\0O\0F\0N\0o\0\0org.freedesktop.NetworkManager\0DeactivateConnection\0A\0active_connection\0I\0o\0\0org.freedesktop.NetworkManager\0Sleep\0A\0sleep\0I\0b\0\0org.freedesktop.NetworkManager\0Enable\0A\0enable\0I\0b\0\0org.freedesktop.NetworkManager\0GetPermissions\0A\0permissions\0O\0F\0N\0a{ss}\0\0org.freedesktop.NetworkManager\0SetLogging\0A\0level\0I\0s\0domains\0I\0s\0\0org.freedesktop.NetworkManager\0GetLogging\0S\0level\0O\0F\0N\0s\0domains\0O\0F\0N\0s\0\0org.freedesktop.NetworkManager\0CheckConnectivity\0A\0connectivity\0O\0F\0N\0u\0\0org.freedesktop.NetworkManager\0state\0S\0state\0O\0F\0N\0u\0\0\0",
 "org.freedesktop.NetworkManager\0CheckPermissions\0org.freedesktop.NetworkManager\0StateChanged\0org.freedesktop.NetworkManager\0PropertiesChanged\0org.freedesktop.NetworkManager\0DeviceAdded\0org.freedesktop.NetworkManager\0DeviceRemoved\0\0",
-"org.freedesktop.NetworkManager\0Devices\0devices\0read\0org.freedesktop.NetworkManager\0NetworkingEnabled\0networking_enabled\0read\0org.freedesktop.NetworkManager\0WirelessEnabled\0wireless_enabled\0readwrite\0org.freedesktop.NetworkManager\0WirelessHardwareEnabled\0wireless_hardware_enabled\0read\0org.freedesktop.NetworkManager\0WwanEnabled\0wwan_enabled\0readwrite\0org.freedesktop.NetworkManager\0WwanHardwareEnabled\0wwan_hardware_enabled\0read\0org.freedesktop.NetworkManager\0WimaxEnabled\0wimax_enabled\0readwrite\0org.freedesktop.NetworkManager\0WimaxHardwareEnabled\0wimax_hardware_enabled\0read\0org.freedesktop.NetworkManager\0ActiveConnections\0active_connections\0read\0org.freedesktop.NetworkManager\0PrimaryConnection\0primary_connection\0read\0org.freedesktop.NetworkManager\0PrimaryConnectionType\0primary_connection_type\0read\0org.freedesktop.NetworkManager\0ActivatingConnection\0activating_connection\0read\0org.freedesktop.NetworkManager\0Startup\0startup\0read\0org.freedesktop.NetworkManager\0Version\0version\0read\0org.freedesktop.NetworkManager\0State\0state\0read\0org.freedesktop.NetworkManager\0Connectivity\0connectivity\0read\0\0"
+"org.freedesktop.NetworkManager\0Devices\0devices\0read\0org.freedesktop.NetworkManager\0NetworkingEnabled\0networking_enabled\0read\0org.freedesktop.NetworkManager\0WirelessEnabled\0wireless_enabled\0readwrite\0org.freedesktop.NetworkManager\0WirelessHardwareEnabled\0wireless_hardware_enabled\0read\0org.freedesktop.NetworkManager\0WwanEnabled\0wwan_enabled\0readwrite\0org.freedesktop.NetworkManager\0WwanHardwareEnabled\0wwan_hardware_enabled\0read\0org.freedesktop.NetworkManager\0WimaxEnabled\0wimax_enabled\0readwrite\0org.freedesktop.NetworkManager\0WimaxHardwareEnabled\0wimax_hardware_enabled\0read\0org.freedesktop.NetworkManager\0ActiveConnections\0active_connections\0read\0org.freedesktop.NetworkManager\0PrimaryConnection\0primary_connection\0read\0org.freedesktop.NetworkManager\0PrimaryConnectionType\0primary_connection_type\0read\0org.freedesktop.NetworkManager\0Metered\0metered\0read\0org.freedesktop.NetworkManager\0ActivatingConnection\0activating_connection\0read\0org.freedesktop.NetworkManager\0Startup\0startup\0read\0org.freedesktop.NetworkManager\0Version\0version\0read\0org.freedesktop.NetworkManager\0State\0state\0read\0org.freedesktop.NetworkManager\0Connectivity\0connectivity\0read\0\0"
 };
 
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 080cdb8b..34ec0819 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -161,6 +161,7 @@ typedef struct {
 	guint ac_cleanup_id;
 	NMActiveConnection *primary_connection;
 	NMActiveConnection *activating_connection;
+	NMMetered metered;
 
 	GSList *devices;
 	NMState state;
@@ -170,7 +171,6 @@ typedef struct {
 	NMPolicy *policy;
 
 	NMDBusManager *dbus_mgr;
-	gboolean       prop_filter_added;
 	NMRfkillManager *rfkill_mgr;
 
 	NMSettings *settings;
@@ -194,6 +194,7 @@ typedef struct {
 	guint timestamp_update_id;
 
 	gboolean startup;
+	gboolean devices_inited;
 } NMManagerPrivate;
 
 #define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
@@ -233,6 +234,7 @@ enum {
 	PROP_PRIMARY_CONNECTION_TYPE,
 	PROP_ACTIVATING_CONNECTION,
 	PROP_DEVICES,
+	PROP_METERED,
 
 	/* Not exported */
 	PROP_HOSTNAME,
@@ -658,6 +660,30 @@ find_best_device_state (NMManager *manager)
 }
 
 static void
+nm_manager_update_metered (NMManager *manager)
+{
+	NMManagerPrivate *priv;
+	NMDevice *device;
+	NMMetered value = NM_METERED_UNKNOWN;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+	priv = NM_MANAGER_GET_PRIVATE (manager);
+
+	if (priv->primary_connection) {
+		device =  nm_active_connection_get_device (priv->primary_connection);
+		if (device)
+			value = nm_device_get_metered (device);
+	}
+
+	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);
+	}
+}
+
+static void
 nm_manager_update_state (NMManager *manager)
 {
 	NMManagerPrivate *priv;
@@ -718,6 +744,9 @@ check_if_startup_complete (NMManager *self)
 	if (!priv->startup)
 		return;
 
+	if (!priv->devices_inited)
+		return;
+
 	if (!nm_settings_get_startup_complete (priv->settings)) {
 		nm_log_dbg (LOGD_CORE, "check_if_startup_complete returns FALSE because of NMSettings");
 		return;
@@ -1159,18 +1188,26 @@ system_hostname_changed_cb (NMSettings *settings,
 	char *hostname;
 
 	hostname = nm_settings_get_hostname (priv->settings);
+
+	/* nm_settings_get_hostname() does not return an empty hostname. */
+	nm_assert (!hostname || *hostname);
+
 	if (!hostname && !priv->hostname)
 		return;
-	if (hostname && priv->hostname && !strcmp (hostname, priv->hostname))
+	if (hostname && priv->hostname && !strcmp (hostname, priv->hostname)) {
+		g_free (hostname);
 		return;
+	}
+
+	/* realloc, to free possibly trailing data after NUL. */
+	if (hostname)
+		hostname = g_realloc (hostname, strlen (hostname) + 1);
 
 	g_free (priv->hostname);
-	priv->hostname = (hostname && strlen (hostname)) ? g_strdup (hostname) : NULL;
+	priv->hostname = hostname;
 	g_object_notify (G_OBJECT (self), NM_MANAGER_HOSTNAME);
 
 	nm_dhcp_manager_set_default_hostname (nm_dhcp_manager_get (), priv->hostname);
-
-	g_free (hostname);
 }
 
 /*******************************************************************/
@@ -4173,6 +4210,8 @@ nm_manager_start (NMManager *self)
 	 */
 	system_create_virtual_devices (self);
 
+	priv->devices_inited = TRUE;
+
 	check_if_startup_complete (self);
 }
 
@@ -4260,6 +4299,14 @@ firmware_dir_changed (GFileMonitor *monitor,
 }
 
 static void
+connection_metered_changed (GObject *object,
+                            NMMetered metered,
+                            gpointer user_data)
+{
+	nm_manager_update_metered (NM_MANAGER (user_data));
+}
+
+static void
 policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
@@ -4281,11 +4328,23 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user
 		ac = NULL;
 
 	if (ac != priv->primary_connection) {
-		g_clear_object (&priv->primary_connection);
+		if (priv->primary_connection) {
+			g_signal_handlers_disconnect_by_func (priv->primary_connection,
+			                                      G_CALLBACK (connection_metered_changed),
+			                                      self);
+			g_clear_object (&priv->primary_connection);
+		}
+
 		priv->primary_connection = ac ? g_object_ref (ac) : NULL;
+
+		if (priv->primary_connection) {
+			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_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);
 	}
 }
 
@@ -4625,17 +4684,13 @@ dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
                             gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
-	gboolean success = FALSE;
+	gboolean success;
 
 	if (dbus_connection) {
-		/* Register property filter on new connection; there's no reason this
-		 * should fail except out-of-memory or program error; if it does fail
-		 * then there's no Manager property access control, which is bad.
-		 */
+		/* Only fails on ENOMEM */
 		success = dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL);
 		g_assert (success);
 	}
-	NM_MANAGER_GET_PRIVATE (self)->prop_filter_added = success;
 }
 
 /**********************************************************************/
@@ -4663,11 +4718,9 @@ nm_manager_new (NMSettings *settings,
                 gboolean initial_net_enabled,
                 gboolean initial_wifi_enabled,
                 gboolean initial_wwan_enabled,
-                gboolean initial_wimax_enabled,
-                GError **error)
+                gboolean initial_wimax_enabled)
 {
 	NMManagerPrivate *priv;
-	DBusGConnection *bus;
 	DBusConnection *dbus_connection;
 	NMConfigData *config_data;
 
@@ -4680,16 +4733,14 @@ nm_manager_new (NMSettings *settings,
 
 	priv = NM_MANAGER_GET_PRIVATE (singleton);
 
-	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	if (!bus) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-		                     "Failed to initialize D-Bus connection");
-		g_object_unref (singleton);
-		return NULL;
-	}
+	dbus_connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
+	if (dbus_connection) {
+		gboolean success;
 
-	dbus_connection = dbus_g_connection_get_connection (bus);
-	g_assert (dbus_connection);
+		/* Only fails on ENOMEM */
+		success = dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL);
+		g_assert (success);
+	}
 
 	priv->policy = nm_policy_new (singleton, settings);
 	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE,
@@ -4714,14 +4765,6 @@ nm_manager_new (NMSettings *settings,
 	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
 	                  G_CALLBACK (connectivity_changed), singleton);
 
-	if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) {
-		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-		                     "Failed to register DBus connection filter");
-		g_object_unref (singleton);
-		return NULL;
-	}
-	priv->prop_filter_added = TRUE;
-
 	priv->settings = g_object_ref (settings);
 	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE,
 	                  G_CALLBACK (settings_startup_complete_changed), singleton);
@@ -4879,6 +4922,8 @@ nm_manager_init (NMManager *manager)
 
 	/* Update timestamps in active connections */
 	priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager);
+
+	priv->metered = NM_METERED_UNKNOWN;
 }
 
 static void
@@ -4963,6 +5008,9 @@ get_property (GObject *object, guint prop_id,
 		}
 		g_value_take_boxed (value, array);
 		break;
+	case PROP_METERED:
+		g_value_set_uint (value, priv->metered);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -5013,7 +5061,6 @@ dispose (GObject *object)
 {
 	NMManager *manager = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	DBusGConnection *bus;
 	DBusConnection *dbus_connection;
 
 	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
@@ -5059,14 +5106,9 @@ dispose (GObject *object)
 
 	/* Unregister property filter */
 	if (priv->dbus_mgr) {
-		bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
-		if (bus) {
-			dbus_connection = dbus_g_connection_get_connection (bus);
-			if (dbus_connection && priv->prop_filter_added) {
-				dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
-				priv->prop_filter_added = FALSE;
-			}
-		}
+		dbus_connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
+		if (dbus_connection)
+			dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
 		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, manager);
 		priv->dbus_mgr = NULL;
 	}
@@ -5238,6 +5280,20 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                     G_PARAM_READABLE |
 		                     G_PARAM_STATIC_STRINGS));
 
+	/**
+	 * NMManager:metered:
+	 *
+	 * Whether the connectivity is metered.
+	 *
+	 * Since: 1.0.6
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_METERED,
+		 g_param_spec_uint (NM_MANAGER_METERED, "", "",
+		                    0, G_MAXUINT32, NM_METERED_UNKNOWN,
+		                    G_PARAM_READABLE |
+		                    G_PARAM_STATIC_STRINGS));
+
 	/* signals */
 	signals[DEVICE_ADDED] =
 		g_signal_new ("device-added",
diff --git a/src/nm-manager.h b/src/nm-manager.h
index 3b00e805..711bcce8 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -51,6 +51,7 @@
 #define NM_MANAGER_PRIMARY_CONNECTION_TYPE "primary-connection-type"
 #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection"
 #define NM_MANAGER_DEVICES "devices"
+#define NM_MANAGER_METERED "metered"
 
 /* Not exported */
 #define NM_MANAGER_HOSTNAME "hostname"
@@ -83,8 +84,7 @@ NMManager *   nm_manager_new                           (NMSettings *settings,
                                                         gboolean initial_net_enabled,
                                                         gboolean initial_wifi_enabled,
                                                         gboolean initial_wwan_enabled,
-                                                        gboolean initial_wimax_enabled,
-                                                        GError **error);
+                                                        gboolean initial_wimax_enabled);
 
 NMManager *   nm_manager_get                           (void);
 
diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c
index 5ee6c865..b392fc17 100644
--- a/src/nm-route-manager.c
+++ b/src/nm-route-manager.c
@@ -18,10 +18,10 @@
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
-#include <string.h>
-
 #include "config.h"
 
+#include <string.h>
+
 #include "nm-route-manager.h"
 #include "nm-platform.h"
 #include "nmp-object.h"
@@ -112,9 +112,17 @@ static const VTableIP vtable_v4, vtable_v6;
 
 /*********************************************************************************************/
 
-#define _LOG_PREFIX_NAME "route-mgr"
-
-#define _LOG(level, addr_family, ...) \
+#define _NMLOG_PREFIX_NAME   "route-mgr"
+#undef  _NMLOG_ENABLED
+#define _NMLOG_ENABLED(level, addr_family) \
+    ({ \
+        const int __addr_family = (addr_family); \
+        const NMLogLevel __level = (level); \
+        const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \
+        \
+        nm_logging_enabled (__level, __domain); \
+    })
+#define _NMLOG(level, addr_family, ...) \
     G_STMT_START { \
         const int __addr_family = (addr_family); \
         const NMLogLevel __level = (level); \
@@ -122,38 +130,17 @@ static const VTableIP vtable_v4, vtable_v6;
         \
         if (nm_logging_enabled (__level, __domain)) { \
             char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \
-            char __prefix[30] = _LOG_PREFIX_NAME; \
+            char __prefix[30] = _NMLOG_PREFIX_NAME; \
             \
             if ((self) != singleton_instance) \
-                g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _LOG_PREFIX_NAME, __ch, (self)); \
+                g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _NMLOG_PREFIX_NAME, __ch, (self)); \
             else \
-                __prefix[STRLEN (_LOG_PREFIX_NAME)] = __ch; \
+                __prefix[STRLEN (_NMLOG_PREFIX_NAME)] = __ch; \
             _nm_log ((level), (__domain), 0, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, addr_family) \
-    ({ \
-        const int __addr_family = (addr_family); \
-        const NMLogLevel __level = (level); \
-        const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \
-        \
-        nm_logging_enabled (__level, __domain); \
-    })
-
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED(addr_family)   _LOG_LEVEL_ENABLED (LOGL_TRACE, addr_family)
-#define _LOGT(addr_family, ...)      _LOG (LOGL_TRACE, addr_family, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED(addr_family)   (FALSE && _LOG_LEVEL_ENABLED (LOGL_TRACE, addr_family))
-#define _LOGT(addr_family, ...)      G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, addr_family, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(addr_family, ...)      _LOG (LOGL_DEBUG, addr_family, __VA_ARGS__)
-#define _LOGI(addr_family, ...)      _LOG (LOGL_INFO , addr_family, __VA_ARGS__)
-#define _LOGW(addr_family, ...)      _LOG (LOGL_WARN , addr_family, __VA_ARGS__)
-#define _LOGE(addr_family, ...)      _LOG (LOGL_ERR  , addr_family, __VA_ARGS__)
 
 /*********************************************************************************************/
 
diff --git a/src/nm-types.h b/src/nm-types.h
index 1a002ac3..5806e15d 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -127,6 +127,12 @@ typedef enum {
 	NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1,
 } NMPObjectType;
 
+typedef enum {
+	NM_IP_CONFIG_MERGE_DEFAULT                  = 0,
+	NM_IP_CONFIG_MERGE_NO_ROUTES                = (1LL << 0),
+	NM_IP_CONFIG_MERGE_NO_DNS                   = (1LL << 1),
+} NMIPConfigMergeFlags;
+
 /* settings */
 typedef struct _NMAgentManager       NMAgentManager;
 typedef struct _NMSecretAgent        NMSecretAgent;
diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf
index 0f1019b9..afbcc720 100644
--- a/src/org.freedesktop.NetworkManager.conf
+++ b/src/org.freedesktop.NetworkManager.conf
@@ -25,6 +25,8 @@
                 <allow send_destination="org.freedesktop.NetworkManager.vpnc"/>
                 <allow send_destination="org.freedesktop.NetworkManager.ssh"/>
                 <allow send_destination="org.freedesktop.NetworkManager.iodine"/>
+                <allow send_destination="org.freedesktop.NetworkManager.l2tp"/>
+                <allow send_destination="org.freedesktop.NetworkManager.libreswan"/>
         </policy>
         <policy context="default">
                 <deny own="org.freedesktop.NetworkManager"/>
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 80757f4e..cd0a4e07 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -67,8 +67,9 @@
 
 /*********************************************************************************************/
 
-#define _LOG_DOMAIN LOGD_PLATFORM
-#define _LOG_PREFIX_NAME "platform-linux"
+#define _NMLOG_DOMAIN                     LOGD_PLATFORM
+#define _NMLOG_PREFIX_NAME                "platform-linux"
+#define _NMLOG(level, ...)                _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__)
 
 #define _LOG(level, domain, self, ...) \
     G_STMT_START { \
@@ -77,11 +78,11 @@
         \
         if (nm_logging_enabled (__level, __domain)) { \
             char __prefix[32]; \
-            const char *__p_prefix = _LOG_PREFIX_NAME; \
+            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
             const void *const __self = (self); \
             \
             if (__self && __self != nm_platform_try_get ()) { \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \
+                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
                 __p_prefix = __prefix; \
             } \
             _nm_log (__level, __domain, 0, \
@@ -89,25 +90,12 @@
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, domain) \
-    ( nm_logging_enabled ((level), (domain)) )
 
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED()     _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN)
-#define _LOGT(...)          _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED()     FALSE
-#define _LOGT(...)          G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, platform, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO , _LOG_DOMAIN, platform, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN , _LOG_DOMAIN, platform, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, platform, __VA_ARGS__)
-
-#define debug(...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, NULL, __VA_ARGS__)
-#define warning(...)    _LOG (LOGL_WARN , _LOG_DOMAIN, NULL, __VA_ARGS__)
-#define error(...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, NULL, __VA_ARGS__)
+#define trace(...)      _LOG (LOGL_TRACE, _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define debug(...)      _LOG (LOGL_DEBUG, _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define info(...)       _LOG (LOGL_INFO,  _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define warning(...)    _LOG (LOGL_WARN , _NMLOG_DOMAIN, NULL, __VA_ARGS__)
+#define error(...)      _LOG (LOGL_ERR  , _NMLOG_DOMAIN, NULL, __VA_ARGS__)
 
 /******************************************************************
  * Forward declarations and enums
@@ -150,8 +138,10 @@ static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObje
 struct libnl_vtable
 {
 	void *handle;
+	void *handle_route;
 
 	int (*f_nl_has_capability) (int capability);
+	int (*f_rtnl_link_get_link_netnsid) (const struct rtnl_link *link, gint32 *out_link_netnsid);
 };
 
 static int
@@ -160,24 +150,25 @@ _nl_f_nl_has_capability (int capability)
 	return FALSE;
 }
 
-static struct libnl_vtable *
+static const struct libnl_vtable *
 _nl_get_vtable (void)
 {
 	static struct libnl_vtable vtable;
 
 	if (G_UNLIKELY (!vtable.f_nl_has_capability)) {
-		void *handle;
-
-		handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD);
-		if (handle) {
-			vtable.handle = handle;
-			vtable.f_nl_has_capability = dlsym (handle, "nl_has_capability");
+		vtable.handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD);
+		if (vtable.handle) {
+			vtable.f_nl_has_capability = dlsym (vtable.handle, "nl_has_capability");
+		}
+		vtable.handle_route = dlopen ("libnl-route-3.so.200", RTLD_LAZY | RTLD_NOLOAD);
+		if (vtable.handle_route) {
+			vtable.f_rtnl_link_get_link_netnsid = dlsym (vtable.handle_route, "rtnl_link_get_link_netnsid");
 		}
 
 		if (!vtable.f_nl_has_capability)
 			vtable.f_nl_has_capability = &_nl_f_nl_has_capability;
 
-		g_return_val_if_fail (vtable.handle, &vtable);
+		trace ("libnl: rtnl_link_get_link_netnsid() %s", vtable.f_rtnl_link_get_link_netnsid ? "supported" : "not supported");
 	}
 
 	return &vtable;
@@ -189,6 +180,26 @@ _nl_has_capability (int capability)
 	return (_nl_get_vtable ()->f_nl_has_capability) (capability);
 }
 
+static int
+_rtnl_link_get_link_netnsid (const struct rtnl_link *link, gint32 *out_link_netnsid)
+{
+	const struct libnl_vtable *vtable;
+
+	g_return_val_if_fail (link, -NLE_INVAL);
+	g_return_val_if_fail (out_link_netnsid, -NLE_INVAL);
+
+	vtable = _nl_get_vtable ();
+	return vtable->f_rtnl_link_get_link_netnsid
+	    ? vtable->f_rtnl_link_get_link_netnsid (link, out_link_netnsid)
+	    : -NLE_OPNOTSUPP;
+}
+
+gboolean
+nm_platform_check_support_libnl_link_netnsid (void)
+{
+	return !!(_nl_get_vtable ()->f_rtnl_link_get_link_netnsid);
+}
+
 /* Automatic deallocation of local variables */
 #define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object)))
 static void
@@ -994,6 +1005,7 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob
 	gboolean completed_from_cache_val = FALSE;
 	gboolean *completed_from_cache = complete_from_cache ? &completed_from_cache_val : NULL;
 	const NMPObject *link_cached = NULL;
+	int parent;
 
 	nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0);
 
@@ -1013,7 +1025,15 @@ _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_ob
 	obj->flags = rtnl_link_get_flags (nlo);
 	obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP);
 	obj->master = rtnl_link_get_master (nlo);
-	obj->parent = rtnl_link_get_link (nlo);
+	parent = rtnl_link_get_link (nlo);
+	if (parent > 0) {
+		gint32 link_netnsid;
+
+		if (_rtnl_link_get_link_netnsid (nlo, &link_netnsid) == 0)
+			obj->parent = NM_PLATFORM_LINK_OTHER_NETNS;
+		else
+			obj->parent = parent;
+	}
 	obj->mtu = rtnl_link_get_mtu (nlo);
 	obj->arptype = rtnl_link_get_arptype (nlo);
 
@@ -2306,11 +2326,25 @@ event_notification (struct nl_msg *msg, gpointer user_data)
 	if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK)
 		_support_user_ipv6ll_detect ((struct rtnl_link *) nlo);
 
-	obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE);
+	switch (msghdr->nlmsg_type) {
+	case RTM_DELADDR:
+	case RTM_DELLINK:
+	case RTM_DELROUTE:
+		/* The event notifies about a deleted object. We don't need to initialize all the
+		 * fields of the nmp-object. Shortcut nmp_object_from_nl(). */
+		obj = nmp_object_from_nl (platform, nlo, TRUE, TRUE);
+		_LOGD ("event-notification: %s, seq %u: %s",
+		       _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
+		       msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
+		break;
+	default:
+		obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE);
+		_LOGD ("event-notification: %s, seq %u: %s",
+		       _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
+		       msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
+		break;
+	}
 
-	_LOGD ("event-notification: %s, seq %u: %s",
-	       _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
-	       msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
 	if (obj) {
 		auto_nmp_obj NMPObject *obj_cache = NULL;
 
@@ -4549,7 +4583,7 @@ event_handler_read_netlink_one (NMPlatform *platform)
 			debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
 			break;
 		case -NLE_NOMEM:
-			warning ("Too many netlink events. Need to resynchronize platform cache");
+			info ("Too many netlink events. Need to resynchronize platform cache");
 			/* Drain the event queue, we've lost events and are out of sync anyway and we'd
 			 * like to free up some space. We'll read in the status synchronously. */
 			_nl_sock_flush_data (priv->nlh_event);
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index d3c62bdd..b7f0947e 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -33,6 +33,7 @@
 #include "nm-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-logging.h"
+#include "nm-setting-wired.h"
 
 
 /******************************************************************
@@ -48,8 +49,14 @@ ethtool_get (const char *name, gpointer edata)
 	if (!name || !*name)
 		return FALSE;
 
+	if (!nmp_utils_device_exists (name))
+		return FALSE;
+
+	/* nmp_utils_device_exists() already errors out if @name is invalid. */
+	nm_assert (strlen (name) < IFNAMSIZ);
+
 	memset (&ifr, 0, sizeof (ifr));
-	strncpy (ifr.ifr_name, name, IFNAMSIZ);
+	strcpy (ifr.ifr_name, name);
 	ifr.ifr_data = edata;
 
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
@@ -255,6 +262,43 @@ nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed)
 	return TRUE;
 }
 
+gboolean
+nmp_utils_ethtool_set_wake_on_lan (const char *ifname,
+                                   NMSettingWiredWakeOnLan wol,
+                                   const char *wol_password)
+{
+	struct ethtool_wolinfo wol_info = { };
+
+	nm_log_dbg (LOGD_PLATFORM, "setting Wake-on-LAN options 0x%x, password '%s'",
+	            (unsigned int) wol, wol_password);
+
+	wol_info.cmd = ETHTOOL_SWOL;
+	wol_info.wolopts = 0;
+
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY))
+		wol_info.wolopts |= WAKE_PHY;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST))
+		wol_info.wolopts |= WAKE_UCAST;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST))
+		wol_info.wolopts |= WAKE_MCAST;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST))
+		wol_info.wolopts |= WAKE_BCAST;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP))
+		wol_info.wolopts |= WAKE_ARP;
+	if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+		wol_info.wolopts |= WAKE_MAGIC;
+
+	if (wol_password) {
+		if (!nm_utils_hwaddr_aton (wol_password, wol_info.sopass, ETH_ALEN)) {
+			nm_log_dbg (LOGD_PLATFORM, "couldn't parse Wake-on-LAN password '%s'", wol_password);
+			return FALSE;
+		}
+		wol_info.wolopts |= WAKE_MAGICSECURE;
+	}
+
+	return ethtool_get (ifname, &wol_info);
+}
+
 /******************************************************************
  * mii
  ******************************************************************/
@@ -270,6 +314,9 @@ nmp_utils_mii_supports_carrier_detect (const char *ifname)
 	if (!ifname)
 		return FALSE;
 
+	if (!nmp_utils_device_exists (ifname))
+		return FALSE;
+
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd < 0) {
 		nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname);
@@ -433,4 +480,17 @@ nmp_utils_lifetime_get (guint32 timestamp,
 	return TRUE;
 }
 
+gboolean
+nmp_utils_device_exists (const char *name)
+{
+#define SYS_CLASS_NET "/sys/class/net/"
+	char sysdir[STRLEN (SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET;
 
+	if (   !name
+	    || strlen (name) >= IFNAMSIZ
+	    || !nm_utils_is_valid_path_component (name))
+		g_return_val_if_reached (FALSE);
+
+	strcpy (&sysdir[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 557de844..3769a8e1 100644
--- a/src/platform/nm-platform-utils.h
+++ b/src/platform/nm-platform-utils.h
@@ -26,6 +26,7 @@
 #include <gudev/gudev.h>
 
 #include "nm-platform.h"
+#include "nm-setting-wired.h"
 
 
 const char *nmp_utils_ethtool_get_driver (const char *ifname);
@@ -33,6 +34,9 @@ gboolean nmp_utils_ethtool_supports_carrier_detect (const char *ifname);
 gboolean nmp_utils_ethtool_supports_vlans (const char *ifname);
 int nmp_utils_ethtool_get_peer_ifindex (const char *ifname);
 gboolean nmp_utils_ethtool_get_wake_on_lan (const char *ifname);
+gboolean nmp_utils_ethtool_set_wake_on_lan (const char *ifname, NMSettingWiredWakeOnLan wol,
+                                            const char *wol_password);
+
 gboolean nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed);
 
 gboolean nmp_utils_ethtool_get_driver_info (const char *ifname,
@@ -63,4 +67,6 @@ gboolean nmp_utils_lifetime_get (guint32 timestamp,
                                  guint32 *out_lifetime,
                                  guint32 *out_preferred);
 
+gboolean nmp_utils_device_exists (const char *name);
+
 #endif /* __NM_PLATFORM_UTILS_H__ */
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index b6d87ff9..fcb5b061 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -43,43 +43,26 @@
 
 G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX);
 
-#define _LOG_DOMAIN LOGD_PLATFORM
-#define _LOG_PREFIX_NAME "platform"
-
-#define _LOG(level, domain, self, ...) \
+#define _NMLOG_DOMAIN           LOGD_PLATFORM
+#define _NMLOG_PREFIX_NAME      "platform"
+#define _NMLOG(level, ...) \
     G_STMT_START { \
         const NMLogLevel __level = (level); \
-        const NMLogDomain __domain = (domain); \
         \
-        if (nm_logging_enabled (__level, __domain)) { \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             char __prefix[32]; \
-            const char *__p_prefix = _LOG_PREFIX_NAME; \
+            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
             const void *const __self = (self); \
             \
             if (__self && __self != nm_platform_try_get ()) { \
-                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \
+                g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
                 __p_prefix = __prefix; \
             } \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, domain) \
-    ( nm_logging_enabled ((level), (domain)) )
-
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED()     _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN)
-#define _LOGT(...)          _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED()     FALSE
-#define _LOGT(...)          G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, self, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO , _LOG_DOMAIN, self, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN , _LOG_DOMAIN, self, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, self, __VA_ARGS__)
 
 #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate))
 
@@ -464,9 +447,12 @@ nm_platform_link_get_all (NMPlatform *self)
 			g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)));
 		}
 		if (item->parent != 0) {
-			g_warn_if_fail (item->parent > 0);
-			g_warn_if_fail (item->parent != item->ifindex);
-			g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
+			if (item->parent != NM_PLATFORM_LINK_OTHER_NETNS) {
+				g_warn_if_fail (item->parent > 0);
+				g_warn_if_fail (item->parent != item->ifindex);
+				g_warn_if_fail (   !nm_platform_check_support_libnl_link_netnsid ()
+				                || g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
+			}
 		}
 	}
 #endif
@@ -1873,7 +1859,7 @@ nm_platform_ip4_address_add (NMPlatform *self,
 	g_return_val_if_fail (klass->ip4_address_add, FALSE);
 	g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP4Address addr = { 0 };
 
 		addr.ifindex = ifindex;
@@ -1909,7 +1895,7 @@ nm_platform_ip6_address_add (NMPlatform *self,
 	g_return_val_if_fail (preferred <= lifetime, FALSE);
 	g_return_val_if_fail (klass->ip6_address_add, FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP6Address addr = { 0 };
 
 		addr.ifindex = ifindex;
@@ -2190,7 +2176,7 @@ nm_platform_ip4_route_add (NMPlatform *self,
 	g_return_val_if_fail (0 <= plen && plen <= 32, FALSE);
 	g_return_val_if_fail (klass->ip4_route_add, FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP4Route route = { 0 };
 
 		route.ifindex = ifindex;
@@ -2218,7 +2204,7 @@ nm_platform_ip6_route_add (NMPlatform *self,
 	g_return_val_if_fail (0 <= plen && plen <= 128, FALSE);
 	g_return_val_if_fail (klass->ip6_route_add, FALSE);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
+	if (_LOGD_ENABLED ()) {
 		NMPlatformIP6Route route = { 0 };
 
 		route.ifindex = ifindex;
@@ -2372,8 +2358,10 @@ nm_platform_link_to_string (const NMPlatformLink *link)
 	else
 		master[0] = 0;
 
-	if (link->parent)
-		g_snprintf (parent, sizeof (master), "@%d", link->parent);
+	if (link->parent > 0)
+		g_snprintf (parent, sizeof (parent), "@%d", link->parent);
+	else if (link->parent == NM_PLATFORM_LINK_OTHER_NETNS)
+		g_strlcpy (parent, "@other-netns", sizeof (parent));
 	else
 		parent[0] = 0;
 
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 4d254195..3219dd5a 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -87,6 +87,8 @@ typedef enum {
 	_NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL,
 } NMPlatformReason;
 
+#define NM_PLATFORM_LINK_OTHER_NETNS    (-1)
+
 #define __NMPlatformObject_COMMON \
 	int ifindex; \
 	;
@@ -105,6 +107,10 @@ struct _NMPlatformLink {
 
 	gboolean initialized;
 	int master;
+
+	/* rtnl_link_get_link(), IFLA_LINK.
+	 * If IFLA_LINK_NETNSID indicates that the parent is in another namespace,
+	 * this field be set to (negative) NM_PLATFORM_LINK_OTHER_NETNS. */
 	int parent;
 
 	/* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */
@@ -734,6 +740,7 @@ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4R
 int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
 
 gboolean nm_platform_check_support_libnl_extended_ifa_flags (void);
+gboolean nm_platform_check_support_libnl_link_netnsid (void);
 gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self);
 gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self);
 
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 51684b93..7fe2d7d3 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -18,10 +18,11 @@
  * Copyright (C) 2015 Red Hat, Inc.
  */
 
-#include "nmp-object.h"
+#include "config.h"
 
 #include <unistd.h>
 
+#include "nmp-object.h"
 #include "nm-platform-utils.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
@@ -29,38 +30,21 @@
 
 /*********************************************************************************************/
 
-#define _LOG_DOMAIN LOGD_PLATFORM
-
-#define _LOG(level, domain, obj, ...) \
+#define _NMLOG_DOMAIN LOGD_PLATFORM
+#define _NMLOG(level, obj, ...) \
     G_STMT_START { \
         const NMLogLevel __level = (level); \
-        const NMLogDomain __domain = (domain); \
         \
-        if (nm_logging_enabled (__level, __domain)) { \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             const NMPObject *const __obj = (obj); \
             \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
                      "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __obj, \
                      (__obj ? NMP_OBJECT_GET_CLASS (__obj)->obj_type_name : "???") \
                      _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
-#define _LOG_LEVEL_ENABLED(level, domain) \
-    ( nm_logging_enabled ((level), (domain)) )
-
-#ifdef NM_MORE_LOGGING
-#define _LOGT_ENABLED()     _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN)
-#define _LOGT(obj, ...)          _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__)
-#else
-#define _LOGT_ENABLED()     FALSE
-#define _LOGT(obj, ...)          G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__); } } G_STMT_END
-#endif
-
-#define _LOGD(obj, ...)      _LOG (LOGL_DEBUG, _LOG_DOMAIN, obj, __VA_ARGS__)
-#define _LOGI(obj, ...)      _LOG (LOGL_INFO , _LOG_DOMAIN, obj, __VA_ARGS__)
-#define _LOGW(obj, ...)      _LOG (LOGL_WARN , _LOG_DOMAIN, obj, __VA_ARGS__)
-#define _LOGE(obj, ...)      _LOG (LOGL_ERR  , _LOG_DOMAIN, obj, __VA_ARGS__)
 
 /*********************************************************************************************/
 
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index dc7b8397..ce371b75 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -231,10 +231,7 @@ test_slave (int master, int type, SignalData *master_changed)
 	g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex));
 	g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0);
 	accept_signals (link_changed, 1, 3);
-	if (link_type != NM_LINK_TYPE_TEAM)
-		accept_signals (master_changed, 1, 2);
-	else
-		accept_signals (master_changed, 1, 1);
+	accept_signals (master_changed, 1, 2);
 
 	ensure_no_signal (master_changed);
 
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
index 8428010a..470b73d1 100644
--- a/src/platform/wifi/wifi-utils-wext.c
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -34,6 +34,7 @@
 #include "wifi-utils-wext.h"
 #include "nm-logging.h"
 #include "nm-utils.h"
+#include "nm-platform-utils.h"
 
 /* Hacks necessary to #include wireless.h; yay for WEXT */
 #ifndef __user
@@ -670,6 +671,9 @@ wifi_wext_is_wifi (const char *iface)
 	struct iwreq iwr;
 	gboolean is_wifi = FALSE;
 
+	if (!nmp_utils_device_exists (iface))
+		return FALSE;
+
 	fd = socket (PF_INET, SOCK_DGRAM, 0);
 	if (fd >= 0) {
 		strncpy (iwr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 0f67a216..da5384c0 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -38,32 +38,57 @@
 #include "nm-properties-changed-signal.h"
 #include "nm-core-internal.h"
 #include "nm-glib-compat.h"
+#include "gsystem-local-alloc.h"
 
 #define SETTINGS_TIMESTAMPS_FILE  NMSTATEDIR "/timestamps"
 #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
 
-static void impl_settings_connection_get_settings (NMSettingsConnection *connection,
+#define _NMLOG_DOMAIN        LOGD_SETTINGS
+#define _NMLOG_PREFIX_NAME   "settings-connection"
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
+            char __prefix[128]; \
+            const char *__p_prefix = _NMLOG_PREFIX_NAME; \
+            const void *const __self = (self); \
+            \
+            if (__self) { \
+                const char *__uuid = nm_connection_get_uuid ((NMConnection *) __self); \
+                \
+                g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, __self, __uuid ? "," : "", __uuid ? __uuid : ""); \
+                __p_prefix = __prefix; \
+            } \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+                     "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+
+static void impl_settings_connection_get_settings (NMSettingsConnection *self,
                                                    DBusGMethodInvocation *context);
 
-static void impl_settings_connection_update (NMSettingsConnection *connection,
+static void impl_settings_connection_update (NMSettingsConnection *self,
                                              GHashTable *new_settings,
                                              DBusGMethodInvocation *context);
 
-static void impl_settings_connection_update_unsaved (NMSettingsConnection *connection,
+static void impl_settings_connection_update_unsaved (NMSettingsConnection *self,
                                                      GHashTable *new_settings,
                                                      DBusGMethodInvocation *context);
 
-static void impl_settings_connection_save (NMSettingsConnection *connection,
+static void impl_settings_connection_save (NMSettingsConnection *self,
                                            DBusGMethodInvocation *context);
 
-static void impl_settings_connection_delete (NMSettingsConnection *connection,
+static void impl_settings_connection_delete (NMSettingsConnection *self,
                                              DBusGMethodInvocation *context);
 
-static void impl_settings_connection_get_secrets (NMSettingsConnection *connection,
+static void impl_settings_connection_get_secrets (NMSettingsConnection *self,
                                                   const gchar *setting_name,
                                                   DBusGMethodInvocation *context);
 
-static void impl_settings_connection_clear_secrets (NMSettingsConnection *connection,
+static void impl_settings_connection_clear_secrets (NMSettingsConnection *self,
                                                     DBusGMethodInvocation *context);
 
 #include "nm-settings-connection-glue.h"
@@ -146,7 +171,7 @@ typedef gboolean (*ForEachSecretFunc) (GHashTableIter *iter,
                                        gpointer user_data);
 
 static void
-for_each_secret (NMConnection *connection,
+for_each_secret (NMConnection *self,
                  GHashTable *secrets,
                  gboolean remove_non_secrets,
                  ForEachSecretFunc callback,
@@ -189,7 +214,7 @@ for_each_secret (NMConnection *connection,
 		 * from the connection data, since flags aren't secrets.  What we're
 		 * iterating here is just the secrets, not a whole connection.
 		 */
-		setting = nm_connection_get_setting_by_name (connection, setting_name);
+		setting = nm_connection_get_setting_by_name (self, setting_name);
 		if (setting == NULL)
 			continue;
 
@@ -488,6 +513,9 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 		nm_utils_log_connection_diff (new_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ");
 
 	nm_connection_replace_settings_from_connection (NM_CONNECTION (self), new_connection);
+
+	_LOGD ("replace settings from connection %p (%s)", new_connection, nm_connection_get_id (NM_CONNECTION (self)));
+
 	nm_settings_connection_set_flags (self,
 	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED,
 	                                  FALSE);
@@ -526,7 +554,7 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 }
 
 static void
-ignore_cb (NMSettingsConnection *connection,
+ignore_cb (NMSettingsConnection *self,
            GError *error,
            gpointer user_data)
 {
@@ -583,49 +611,49 @@ commit_changes (NMSettingsConnection *self,
 }
 
 void
-nm_settings_connection_commit_changes (NMSettingsConnection *connection,
+nm_settings_connection_commit_changes (NMSettingsConnection *self,
                                        NMSettingsConnectionCommitFunc callback,
                                        gpointer user_data)
 {
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
-	if (NM_SETTINGS_CONNECTION_GET_CLASS (connection)->commit_changes) {
-		NM_SETTINGS_CONNECTION_GET_CLASS (connection)->commit_changes (connection,
-		                                                               callback ? callback : ignore_cb,
-		                                                               user_data);
+	if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes) {
+		NM_SETTINGS_CONNECTION_GET_CLASS (self)->commit_changes (self,
+		                                                         callback ? callback : ignore_cb,
+		                                                         user_data);
 	} else {
 		GError *error = g_error_new (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_FAILED,
 		                             "%s: %s:%d commit_changes() unimplemented", __func__, __FILE__, __LINE__);
 		if (callback)
-			callback (connection, error, user_data);
+			callback (self, error, user_data);
 		g_error_free (error);
 	}
 }
 
 void
-nm_settings_connection_delete (NMSettingsConnection *connection,
+nm_settings_connection_delete (NMSettingsConnection *self,
                                NMSettingsConnectionDeleteFunc callback,
                                gpointer user_data)
 {
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
-	if (NM_SETTINGS_CONNECTION_GET_CLASS (connection)->delete) {
-		NM_SETTINGS_CONNECTION_GET_CLASS (connection)->delete (connection,
-		                                                       callback ? callback : ignore_cb,
-		                                                       user_data);
+	if (NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete) {
+		NM_SETTINGS_CONNECTION_GET_CLASS (self)->delete (self,
+		                                                 callback ? callback : ignore_cb,
+		                                                 user_data);
 	} else {
 		GError *error = g_error_new (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_FAILED,
 		                             "%s: %s:%d delete() unimplemented", __func__, __FILE__, __LINE__);
 		if (callback)
-			callback (connection, error, user_data);
+			callback (self, error, user_data);
 		g_error_free (error);
 	}
 }
 
 static void
-remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
+remove_entry_from_db (NMSettingsConnection *self, const char* db_name)
 {
 	GKeyFile *key_file;
 	const char *db_file;
@@ -644,7 +672,7 @@ remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
 		gsize len;
 		GError *error = NULL;
 
-		connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+		connection_uuid = nm_connection_get_uuid (NM_CONNECTION (self));
 
 		g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
 		data = g_key_file_to_data (key_file, &len, &error);
@@ -653,7 +681,7 @@ remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
 			g_free (data);
 		}
 		if (error) {
-			nm_log_warn (LOGD_SETTINGS, "error writing %s file '%s': %s", db_name, db_file, error->message);
+			_LOGW ("error writing %s file '%s': %s", db_name, db_file, error->message);
 			g_error_free (error);
 		}
 	}
@@ -661,39 +689,39 @@ remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
 }
 
 static void
-do_delete (NMSettingsConnection *connection,
+do_delete (NMSettingsConnection *self,
            NMSettingsConnectionDeleteFunc callback,
            gpointer user_data)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	NMConnection *for_agents;
 
-	g_object_ref (connection);
-	set_visible (connection, FALSE);
+	g_object_ref (self);
+	set_visible (self, FALSE);
 
 	/* Tell agents to remove secrets for this connection */
-	for_agents = nm_simple_connection_new_clone (NM_CONNECTION (connection));
+	for_agents = nm_simple_connection_new_clone (NM_CONNECTION (self));
 	nm_connection_clear_secrets (for_agents);
 	nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents);
 	g_object_unref (for_agents);
 
 	/* Remove timestamp from timestamps database file */
-	remove_entry_from_db (connection, "timestamps");
+	remove_entry_from_db (self, "timestamps");
 
 	/* Remove connection from seen-bssids database file */
-	remove_entry_from_db (connection, "seen-bssids");
+	remove_entry_from_db (self, "seen-bssids");
 
-	nm_settings_connection_signal_remove (connection);
+	nm_settings_connection_signal_remove (self);
 
-	callback (connection, NULL, user_data);
+	callback (self, NULL, user_data);
 
-	g_object_unref (connection);
+	g_object_unref (self);
 }
 
 /**************************************************************/
 
 static gboolean
-supports_secrets (NMSettingsConnection *connection, const char *setting_name)
+supports_secrets (NMSettingsConnection *self, const char *setting_name)
 {
 	/* All secrets supported */
 	return TRUE;
@@ -734,13 +762,13 @@ has_system_owned_secrets (GHashTableIter *iter,
 }
 
 static void
-new_secrets_commit_cb (NMSettingsConnection *connection,
+new_secrets_commit_cb (NMSettingsConnection *self,
                        GError *error,
                        gpointer user_data)
 {
 	if (error) {
-		nm_log_warn (LOGD_SETTINGS, "Error saving new secrets to backing storage: (%d) %s",
-		             error->code, error->message ? error->message : "(unknown)");
+		_LOGW ("Error saving new secrets to backing storage: (%d) %s",
+		       error->code, error->message ? error->message : "(unknown)");
 	}
 }
 
@@ -767,12 +795,11 @@ agent_secrets_done_cb (NMAgentManager *manager,
 	gboolean agent_had_system = FALSE;
 
 	if (error) {
-		nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request error: (%d) %s",
-		            nm_connection_get_uuid (NM_CONNECTION (self)),
-		            setting_name,
-		            call_id,
-		            error->code,
-		            error->message ? error->message : "(unknown)");
+		_LOGD ("(%s:%u) secrets request error: (%d) %s",
+		       setting_name,
+		       call_id,
+		       error->code,
+		       error->message ? error->message : "(unknown)");
 
 		callback (self, call_id, NULL, setting_name, error, callback_data);
 		return;
@@ -789,11 +816,10 @@ agent_secrets_done_cb (NMAgentManager *manager,
 
 	g_assert (secrets);
 	if (agent_dbus_owner) {
-		nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets returned from agent %s",
-		            nm_connection_get_uuid (NM_CONNECTION (self)),
-		            setting_name,
-		            call_id,
-		            agent_dbus_owner);
+		_LOGD ("(%s:%u) secrets returned from agent %s",
+		       setting_name,
+		       call_id,
+		       agent_dbus_owner);
 
 		/* If the agent returned any system-owned secrets (initial connect and no
 		 * secrets given when the connection was created, or something like that)
@@ -807,36 +833,32 @@ agent_secrets_done_cb (NMAgentManager *manager,
 				/* No user interaction was allowed when requesting secrets; the
 				 * agent is being bad.  Remove system-owned secrets.
 				 */
-				nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) interaction forbidden but agent %s returned system secrets",
-				            nm_connection_get_uuid (NM_CONNECTION (self)),
-				            setting_name,
-				            call_id,
-				            agent_dbus_owner);
+				_LOGD ("(%s:%u) interaction forbidden but agent %s returned system secrets",
+				       setting_name,
+				       call_id,
+				       agent_dbus_owner);
 
 				for_each_secret (NM_CONNECTION (self), secrets, FALSE, clear_nonagent_secrets, NULL);
 			} else if (agent_has_modify == FALSE) {
 				/* Agent didn't successfully authenticate; clear system-owned secrets
 				 * from the secrets the agent returned.
 				 */
-				nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) agent failed to authenticate but provided system secrets",
-				            nm_connection_get_uuid (NM_CONNECTION (self)),
-				            setting_name,
-				            call_id);
+				_LOGD ("(%s:%u) agent failed to authenticate but provided system secrets",
+				       setting_name,
+				       call_id);
 
 				for_each_secret (NM_CONNECTION (self), secrets, FALSE, clear_nonagent_secrets, NULL);
 			}
 		}
 	} else {
-		nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) existing secrets returned",
-		            nm_connection_get_uuid (NM_CONNECTION (self)),
-		            setting_name,
-		            call_id);
+		_LOGD ("(%s:%u) existing secrets returned",
+		       setting_name,
+		       call_id);
 	}
 
-	nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request completed",
-	            nm_connection_get_uuid (NM_CONNECTION (self)),
-	            setting_name,
-	            call_id);
+	_LOGD ("(%s:%u) secrets request completed",
+	       setting_name,
+	       call_id);
 
 	/* If no user interaction was allowed, make sure that no "unsaved" secrets
 	 * came back.  Unsaved secrets by definition require user interaction.
@@ -869,34 +891,30 @@ agent_secrets_done_cb (NMAgentManager *manager,
 			 * nothing has changed, since agent-owned secrets don't get saved here.
 			 */
 			if (agent_had_system) {
-				nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) saving new secrets to backing storage",
-						    nm_connection_get_uuid (NM_CONNECTION (self)),
-						    setting_name,
-						    call_id);
+				_LOGD ("(%s:%u) saving new secrets to backing storage",
+				       setting_name,
+				       call_id);
 
 				nm_settings_connection_commit_changes (self, new_secrets_commit_cb, NULL);
 			} else {
-				nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) new agent secrets processed",
-						    nm_connection_get_uuid (NM_CONNECTION (self)),
-						    setting_name,
-						    call_id);
+				_LOGD ("(%s:%u) new agent secrets processed",
+				       setting_name,
+				       call_id);
 			}
 		} else {
-			nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with agent secrets: (%d) %s",
-			            nm_connection_get_uuid (NM_CONNECTION (self)),
-			            setting_name,
-			            call_id,
-			            local ? local->code : -1,
-			            (local && local->message) ? local->message : "(unknown)");
+			_LOGD ("(%s:%u) failed to update with agent secrets: (%d) %s",
+			       setting_name,
+			       call_id,
+			       local ? local->code : -1,
+			       (local && local->message) ? local->message : "(unknown)");
 		}
 		g_variant_unref (secrets_dict);
 	} else {
-		nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with existing secrets: (%d) %s",
-		            nm_connection_get_uuid (NM_CONNECTION (self)),
-		            setting_name,
-		            call_id,
-		            local ? local->code : -1,
-		            (local && local->message) ? local->message : "(unknown)");
+		_LOGD ("(%s:%u) failed to update with existing secrets: (%d) %s",
+		       setting_name,
+		       call_id,
+		       local ? local->code : -1,
+		       (local && local->message) ? local->message : "(unknown)");
 	}
 
 	callback (self, call_id, agent_username, setting_name, local, callback_data);
@@ -907,7 +925,7 @@ agent_secrets_done_cb (NMAgentManager *manager,
 
 /**
  * nm_settings_connection_get_secrets:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  * @subject: the #NMAuthSubject originating the request
  * @setting_name: the setting to return secrets for
  * @flags: flags to modify the secrets request
@@ -935,7 +953,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	GVariant *existing_secrets;
 	GHashTable *existing_secrets_hash;
 	guint32 call_id = 0;
-	char *joined_hints = NULL;
+	gs_free char *joined_hints = NULL;
 
 	/* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
 	 * will clear secrets on this object's settings.
@@ -973,17 +991,11 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	if (existing_secrets)
 		g_variant_unref (existing_secrets);
 
-	if (nm_logging_enabled (LOGL_DEBUG, LOGD_SETTINGS)) {
-		if (hints)
-			joined_hints = g_strjoinv (",", (char **) hints);
-		nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets requested flags 0x%X hints '%s'",
-		            nm_connection_get_uuid (NM_CONNECTION (self)),
-		            setting_name,
-		            call_id,
-		            flags,
-		            joined_hints ? joined_hints : "(none)");
-		g_free (joined_hints);
-	}
+	_LOGD ("(%s:%u) secrets requested flags 0x%X hints '%s'",
+	       setting_name,
+	       call_id,
+	       flags,
+	       (hints && hints[0]) ? (joined_hints = g_strjoinv (",", (char **) hints)) : "(none)");
 
 	return call_id;
 }
@@ -994,9 +1006,8 @@ nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_SETTINGS, "(%s:%u) secrets canceled",
-	            nm_connection_get_uuid (NM_CONNECTION (self)),
-	            call_id);
+	_LOGD ("(%u) secrets canceled",
+	       call_id);
 
 	priv->reqs = g_slist_remove (priv->reqs, GUINT_TO_POINTER (call_id));
 	nm_agent_manager_cancel_secrets (priv->agent_mgr, call_id);
@@ -1004,7 +1015,7 @@ nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
 
 /**** User authorization **************************************/
 
-typedef void (*AuthCallback) (NMSettingsConnection *connection, 
+typedef void (*AuthCallback) (NMSettingsConnection *self,
                               DBusGMethodInvocation *context,
                               NMAuthSubject *subject,
                               GError *error,
@@ -1136,13 +1147,13 @@ auth_start (NMSettingsConnection *self,
 /**** DBus method handlers ************************************/
 
 static gboolean
-check_writable (NMConnection *connection, GError **error)
+check_writable (NMConnection *self, GError **error)
 {
 	NMSettingConnection *s_con;
 
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (self), FALSE);
 
-	s_con = nm_connection_get_setting_connection (connection);
+	s_con = nm_connection_get_setting_connection (self);
 	if (!s_con) {
 		g_set_error_literal (error,
 		                     NM_SETTINGS_ERROR,
@@ -1272,11 +1283,11 @@ has_some_secrets_cb (NMSetting *setting,
 }
 
 static gboolean
-any_secrets_present (NMConnection *connection)
+any_secrets_present (NMConnection *self)
 {
 	gboolean has_secrets = FALSE;
 
-	nm_connection_for_each_setting_value (connection, has_some_secrets_cb, &has_secrets);
+	nm_connection_for_each_setting_value (self, has_some_secrets_cb, &has_secrets);
 	return has_secrets;
 }
 
@@ -1515,7 +1526,7 @@ impl_settings_connection_save (NMSettingsConnection *self,
 }
 
 static void
-con_delete_cb (NMSettingsConnection *connection,
+con_delete_cb (NMSettingsConnection *self,
                GError *error,
                gpointer user_data)
 {
@@ -1528,7 +1539,7 @@ con_delete_cb (NMSettingsConnection *connection,
 }
 
 static void
-delete_auth_cb (NMSettingsConnection *self, 
+delete_auth_cb (NMSettingsConnection *self,
                 DBusGMethodInvocation *context,
                 NMAuthSubject *subject,
                 GError *error,
@@ -1543,7 +1554,7 @@ delete_auth_cb (NMSettingsConnection *self,
 }
 
 static const char *
-get_modify_permission_basic (NMSettingsConnection *connection)
+get_modify_permission_basic (NMSettingsConnection *self)
 {
 	NMSettingConnection *s_con;
 
@@ -1551,7 +1562,7 @@ get_modify_permission_basic (NMSettingsConnection *connection)
 	 * we use the 'modify.own' permission instead of 'modify.system'.  If the
 	 * request affects more than just the caller, require 'modify.system'.
 	 */
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
+	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
 	g_assert (s_con);
 	if (nm_setting_connection_get_num_permissions (s_con) == 1)
 		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
@@ -1692,7 +1703,7 @@ clear_secrets_cb (NMSettingsConnection *self,
 }
 
 static void
-dbus_clear_secrets_auth_cb (NMSettingsConnection *self, 
+dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
                             DBusGMethodInvocation *context,
                             NMAuthSubject *subject,
                             GError *error,
@@ -1815,7 +1826,7 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn
 
 /**
  * nm_settings_connection_get_timestamp:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  * @out_timestamp: the connection's timestamp
  *
  * Returns the time (in seconds since the Unix epoch) when the connection
@@ -1824,19 +1835,19 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn
  * Returns: %TRUE if the timestamp has ever been set, otherwise %FALSE.
  **/
 gboolean
-nm_settings_connection_get_timestamp (NMSettingsConnection *connection,
+nm_settings_connection_get_timestamp (NMSettingsConnection *self,
                                       guint64 *out_timestamp)
 {
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 
 	if (out_timestamp)
-		*out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->timestamp;
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->timestamp_set;
+		*out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set;
 }
 
 /**
  * nm_settings_connection_update_timestamp:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  * @timestamp: timestamp to set into the connection and to store into
  * the timestamps database
  * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage
@@ -1844,18 +1855,18 @@ nm_settings_connection_get_timestamp (NMSettingsConnection *connection,
  * Updates the connection and timestamps database with the provided timestamp.
  **/
 void
-nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
+nm_settings_connection_update_timestamp (NMSettingsConnection *self,
                                          guint64 timestamp,
                                          gboolean flush_to_disk)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	const char *connection_uuid;
 	GKeyFile *timestamps_file;
 	char *data, *tmp;
 	gsize len;
 	GError *error = NULL;
 
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
 	/* Update timestamp in private storage */
 	priv->timestamp = timestamp;
@@ -1868,11 +1879,11 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
 	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))
-			nm_log_warn (LOGD_SETTINGS, "error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
+			_LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
 		g_clear_error (&error);
 	}
 
-	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (self));
 	tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
 	g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp);
 	g_free (tmp);
@@ -1883,7 +1894,7 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
 		g_free (data);
 	}
 	if (error) {
-		nm_log_warn (LOGD_SETTINGS, "error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
+		_LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
 		g_error_free (error);
 	}
 	g_key_file_free (timestamps_file);
@@ -1891,27 +1902,27 @@ nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
 
 /**
  * nm_settings_connection_read_and_fill_timestamp:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  *
  * Retrieves timestamp of the connection's last usage from database file and
  * stores it into the connection private data.
  **/
 void
-nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection)
+nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	const char *connection_uuid;
 	guint64 timestamp = 0;
 	GKeyFile *timestamps_file;
 	GError *err = NULL;
 	char *tmp_str;
 
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
 	/* Get timestamp from database file */
 	timestamps_file = g_key_file_new ();
 	g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL);
-	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (self));
 	tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &err);
 	if (tmp_str) {
 		timestamp = g_ascii_strtoull (tmp_str, NULL, 10);
@@ -1923,8 +1934,8 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection
 		priv->timestamp = timestamp;
 		priv->timestamp_set = TRUE;
 	} else {
-		nm_log_dbg (LOGD_SETTINGS, "failed to read connection timestamp for '%s': (%d) %s",
-		            connection_uuid, err->code, err->message);
+		_LOGD ("failed to read connection timestamp: (%d) %s",
+		       err->code, err->message);
 		g_clear_error (&err);
 	}
 	g_key_file_free (timestamps_file);
@@ -1932,7 +1943,7 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection
 
 /**
  * nm_settings_connection_get_seen_bssids:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  *
  * Returns current list of seen BSSIDs for the connection.
  *
@@ -1940,14 +1951,14 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection
  * The caller is responsible for freeing the list, but not the content.
  **/
 char **
-nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection)
+nm_settings_connection_get_seen_bssids (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	GHashTableIter iter;
 	char **bssids, *bssid;
 	int i;
 
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
 
 	bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1);
 
@@ -1962,34 +1973,34 @@ nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection)
 
 /**
  * nm_settings_connection_has_seen_bssid:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  * @bssid: the BSSID to check the seen BSSID list for
  *
  * Returns: %TRUE if the given @bssid is in the seen BSSIDs list
  **/
 gboolean
-nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
+nm_settings_connection_has_seen_bssid (NMSettingsConnection *self,
                                        const char *bssid)
 {
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 	g_return_val_if_fail (bssid != NULL, FALSE);
 
-	return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->seen_bssids, bssid);
+	return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid);
 }
 
 /**
  * nm_settings_connection_add_seen_bssid:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  * @seen_bssid: BSSID to set into the connection and to store into
  * the seen-bssids database
  *
  * Updates the connection and seen-bssids database with the provided BSSID.
  **/
 void
-nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
+nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
                                        const char *seen_bssid)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	const char *connection_uuid;
 	GKeyFile *seen_bssids_file;
 	char *data, *bssid_str;
@@ -2020,13 +2031,13 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
 	g_key_file_set_list_separator (seen_bssids_file, ',');
 	if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
 		if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
-			nm_log_warn (LOGD_SETTINGS, "error parsing seen-bssids file '%s': %s",
-			             SETTINGS_SEEN_BSSIDS_FILE, error->message);
+			_LOGW ("error parsing seen-bssids file '%s': %s",
+			       SETTINGS_SEEN_BSSIDS_FILE, error->message);
 		}
 		g_clear_error (&error);
 	}
 
-	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+	connection_uuid = nm_connection_get_uuid (NM_CONNECTION (self));
 	g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
 	g_free (list);
 
@@ -2038,23 +2049,23 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
 	g_key_file_free (seen_bssids_file);
 
 	if (error) {
-		nm_log_warn (LOGD_SETTINGS, "error saving seen-bssids to file '%s': %s",
-		             SETTINGS_SEEN_BSSIDS_FILE, error->message);
+		_LOGW ("error saving seen-bssids to file '%s': %s",
+		       SETTINGS_SEEN_BSSIDS_FILE, error->message);
 		g_error_free (error);
 	}
 }
 
 /**
  * nm_settings_connection_read_and_fill_seen_bssids:
- * @connection: the #NMSettingsConnection
+ * @self: the #NMSettingsConnection
  *
  * Retrieves seen BSSIDs of the connection from database file and stores then into the
  * connection private data.
  **/
 void
-nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection)
+nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	const char *connection_uuid;
 	GKeyFile *seen_bssids_file;
 	char **tmp_strv = NULL;
@@ -2065,7 +2076,7 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connecti
 	seen_bssids_file = g_key_file_new ();
 	g_key_file_set_list_separator (seen_bssids_file, ',');
 	if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
-		connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+		connection_uuid = nm_connection_get_uuid (NM_CONNECTION (self));
 		tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
 	}
 	g_key_file_free (seen_bssids_file);
@@ -2082,7 +2093,7 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connecti
 		 * seen-bssids list from the deprecated seen-bssids property of the
 		 * wifi setting.
 		 */
-		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
+		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (self));
 		if (s_wifi) {
 			len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
 			for (i = 0; i < len; i++) {
@@ -2098,16 +2109,16 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connecti
 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
 
 int
-nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *connection)
+nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_retries;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retries;
 }
 
 void
-nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *connection,
+nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self,
                                                 int retries)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	priv->autoconnect_retries = retries;
 	if (retries)
@@ -2117,34 +2128,34 @@ nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *connection
 }
 
 void
-nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *connection)
+nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self)
 {
-	nm_settings_connection_set_autoconnect_retries (connection, AUTOCONNECT_RETRIES_DEFAULT);
+	nm_settings_connection_set_autoconnect_retries (self, AUTOCONNECT_RETRIES_DEFAULT);
 }
 
 gint32
-nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *connection)
+nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_retry_time;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time;
 }
 
 NMDeviceStateReason
-nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *connection)
+nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_blocked_reason;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason;
 }
 
 void
-nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *connection,
+nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
                                                        NMDeviceStateReason reason)
 {
-	NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->autoconnect_blocked_reason = reason;
+	NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason;
 }
 
 gboolean
-nm_settings_connection_can_autoconnect (NMSettingsConnection *connection)
+nm_settings_connection_can_autoconnect (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	NMSettingConnection *s_con;
 	const char *permission;
 
@@ -2153,13 +2164,13 @@ nm_settings_connection_can_autoconnect (NMSettingsConnection *connection)
 	    || priv->autoconnect_blocked_reason != NM_DEVICE_STATE_REASON_NONE)
 		return FALSE;
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
+	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
 	if (!nm_setting_connection_get_autoconnect (s_con))
 		return FALSE;
 
-	permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (connection));
+	permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (self));
 	if (permission) {
-		if (nm_settings_connection_check_permission (connection, permission) == FALSE)
+		if (nm_settings_connection_check_permission (self, permission) == FALSE)
 			return FALSE;
 	}
 
@@ -2168,89 +2179,89 @@ nm_settings_connection_can_autoconnect (NMSettingsConnection *connection)
 
 /**
  * nm_settings_connection_get_nm_generated:
- * @connection: an #NMSettingsConnection
+ * @self: an #NMSettingsConnection
  *
- * Gets the "nm-generated" flag on @connection.
+ * Gets the "nm-generated" flag on @self.
  *
  * A connection is "nm-generated" if it was generated by
  * nm_device_generate_connection() and has not been modified or
  * saved by the user since then.
  */
 gboolean
-nm_settings_connection_get_nm_generated (NMSettingsConnection *connection)
+nm_settings_connection_get_nm_generated (NMSettingsConnection *self)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED);
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED);
 }
 
 /**
  * nm_settings_connection_get_nm_generated_assumed:
- * @connection: an #NMSettingsConnection
+ * @self: an #NMSettingsConnection
  *
- * Gets the "nm-generated-assumed" flag on @connection.
+ * Gets the "nm-generated-assumed" flag on @self.
  *
  * The connection is a generated connection especially
  * generated for connection assumption.
  */
 gboolean
-nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *connection)
+nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
 }
 
 gboolean
-nm_settings_connection_get_ready (NMSettingsConnection *connection)
+nm_settings_connection_get_ready (NMSettingsConnection *self)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->ready;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready;
 }
 
 void
-nm_settings_connection_set_ready (NMSettingsConnection *connection,
+nm_settings_connection_set_ready (NMSettingsConnection *self,
                                   gboolean ready)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	ready = !!ready;
 	if (priv->ready != ready) {
 		priv->ready = ready;
-		g_object_notify (G_OBJECT (connection), NM_SETTINGS_CONNECTION_READY);
+		g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_READY);
 	}
 }
 
 /**
  * nm_settings_connection_set_filename:
- * @connection: an #NMSettingsConnection
- * @filename: @connection's filename
+ * @self: an #NMSettingsConnection
+ * @filename: @self's filename
  *
- * Called by a backend to sets the filename that @connection is read
+ * Called by a backend to sets the filename that @self is read
  * from/written to.
  */
 void
-nm_settings_connection_set_filename (NMSettingsConnection *connection,
+nm_settings_connection_set_filename (NMSettingsConnection *self,
                                      const char *filename)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	if (g_strcmp0 (filename, priv->filename) != 0) {
 		g_free (priv->filename);
 		priv->filename = g_strdup (filename);
-		g_object_notify (G_OBJECT (connection), NM_SETTINGS_CONNECTION_FILENAME);
+		g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_FILENAME);
 	}
 }
 
 /**
  * nm_settings_connection_get_filename:
- * @connection: an #NMSettingsConnection
+ * @self: an #NMSettingsConnection
  *
- * Gets the filename that @connection was read from/written to.  This may be
- * %NULL if @connection is unsaved, or if it is associated with a backend that
+ * Gets the filename that @self was read from/written to.  This may be
+ * %NULL if @self is unsaved, or if it is associated with a backend that
  * does not store each connection in a separate file.
  *
- * Returns: @connection's filename.
+ * Returns: @self's filename.
  */
 const char *
-nm_settings_connection_get_filename (NMSettingsConnection *connection)
+nm_settings_connection_get_filename (NMSettingsConnection *self)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	return priv->filename;
 }
@@ -2283,12 +2294,24 @@ nm_settings_connection_init (NMSettingsConnection *self)
 }
 
 static void
+constructed (GObject *object)
+{
+	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
+
+	_LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self));
+
+	G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object);
+}
+
+static void
 dispose (GObject *object)
 {
 	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	GSList *iter;
 
+	_LOGD ("disposing");
+
 	if (priv->updated_idle_id) {
 		g_source_remove (priv->updated_idle_id);
 		priv->updated_idle_id = 0;
@@ -2389,6 +2412,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	g_type_class_add_private (class, sizeof (NMSettingsConnectionPrivate));
 
 	/* Virtual methods */
+	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
@@ -2439,7 +2463,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	/* Signals */
 
 	/* Emitted when the connection is changed for any reason */
-	signals[UPDATED] = 
+	signals[UPDATED] =
 		g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
 		              G_TYPE_FROM_CLASS (class),
 		              G_SIGNAL_RUN_FIRST,
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 49661f38..512112f0 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -81,11 +81,11 @@ typedef enum
 
 typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass;
 
-typedef void (*NMSettingsConnectionCommitFunc) (NMSettingsConnection *connection,
+typedef void (*NMSettingsConnectionCommitFunc) (NMSettingsConnection *self,
                                                 GError *error,
                                                 gpointer user_data);
 
-typedef void (*NMSettingsConnectionDeleteFunc) (NMSettingsConnection *connection,
+typedef void (*NMSettingsConnectionDeleteFunc) (NMSettingsConnection *self,
                                                 GError *error,
                                                 gpointer user_data);
 
@@ -97,26 +97,26 @@ struct _NMSettingsConnectionClass {
 	GObjectClass parent;
 
 	/* virtual methods */
-	void (*replace_and_commit) (NMSettingsConnection *connection,
+	void (*replace_and_commit) (NMSettingsConnection *self,
 	                            NMConnection *new_connection,
 	                            NMSettingsConnectionCommitFunc callback,
 	                            gpointer user_data);
 
-	void (*commit_changes) (NMSettingsConnection *connection,
+	void (*commit_changes) (NMSettingsConnection *self,
 	                        NMSettingsConnectionCommitFunc callback,
 	                        gpointer user_data);
 
-	void (*delete) (NMSettingsConnection *connection,
+	void (*delete) (NMSettingsConnection *self,
 	                NMSettingsConnectionDeleteFunc callback,
 	                gpointer user_data);
 
-	gboolean (*supports_secrets) (NMSettingsConnection *connection,
+	gboolean (*supports_secrets) (NMSettingsConnection *self,
 	                              const char *setting_name);
 };
 
 GType nm_settings_connection_get_type (void);
 
-void nm_settings_connection_commit_changes (NMSettingsConnection *connection,
+void nm_settings_connection_commit_changes (NMSettingsConnection *self,
                                             NMSettingsConnectionCommitFunc callback,
                                             gpointer user_data);
 
@@ -131,18 +131,18 @@ void nm_settings_connection_replace_and_commit (NMSettingsConnection *self,
                                                 NMSettingsConnectionCommitFunc callback,
                                                 gpointer user_data);
 
-void nm_settings_connection_delete (NMSettingsConnection *connection,
+void nm_settings_connection_delete (NMSettingsConnection *self,
                                     NMSettingsConnectionDeleteFunc callback,
                                     gpointer user_data);
 
-typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *connection,
+typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self,
                                                  guint32 call_id,
                                                  const char *agent_username,
                                                  const char *setting_name,
                                                  GError *error,
                                                  gpointer user_data);
 
-guint32 nm_settings_connection_get_secrets (NMSettingsConnection *connection,
+guint32 nm_settings_connection_get_secrets (NMSettingsConnection *self,
                                             NMAuthSubject *subject,
                                             const char *setting_name,
                                             NMSecretAgentGetSecretsFlags flags,
@@ -151,7 +151,7 @@ guint32 nm_settings_connection_get_secrets (NMSettingsConnection *connection,
                                             gpointer callback_data,
                                             GError **error);
 
-void nm_settings_connection_cancel_secrets (NMSettingsConnection *connection,
+void nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
                                             guint32 call_id);
 
 gboolean nm_settings_connection_is_visible (NMSettingsConnection *self);
@@ -165,52 +165,52 @@ void nm_settings_connection_signal_remove (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self);
 
-NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection *connection);
-NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *connection, NMSettingsConnectionFlags flags, gboolean set);
-NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnection *connection, NMSettingsConnectionFlags flags);
+NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection *self);
+NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set);
+NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags);
 
-gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *connection,
+gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self,
                                                guint64 *out_timestamp);
 
-void nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
+void nm_settings_connection_update_timestamp (NMSettingsConnection *self,
                                               guint64 timestamp,
                                               gboolean flush_to_disk);
 
-void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection);
+void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self);
 
-char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *connection);
+char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *self);
 
-gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
+gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self,
                                                 const char *bssid);
 
-void nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
+void nm_settings_connection_add_seen_bssid (NMSettingsConnection *self,
                                             const char *seen_bssid);
 
-void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection);
+void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self);
 
-int nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *connection);
-void nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *connection,
+int nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self);
+void nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self,
                                                      int retries);
-void nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *connection);
+void nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *self);
 
-gint32 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *connection);
+gint32 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self);
 
-NMDeviceStateReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *connection);
-void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *connection,
+NMDeviceStateReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self);
+void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
                                                             NMDeviceStateReason reason);
 
-gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *connection);
+gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *self);
 
-gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *connection);
-gboolean nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *connection);
+gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *self);
+gboolean nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self);
 
-gboolean nm_settings_connection_get_ready (NMSettingsConnection *connection);
-void     nm_settings_connection_set_ready (NMSettingsConnection *connection,
+gboolean nm_settings_connection_get_ready (NMSettingsConnection *self);
+void     nm_settings_connection_set_ready (NMSettingsConnection *self,
                                            gboolean ready);
 
-void        nm_settings_connection_set_filename (NMSettingsConnection *connection,
+void        nm_settings_connection_set_filename (NMSettingsConnection *self,
                                                  const char *filename);
-const char *nm_settings_connection_get_filename (NMSettingsConnection *connection);
+const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
 
 G_END_DECLS
 
diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
index a18920c6..9499d8ce 100644
--- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c
@@ -67,6 +67,8 @@ typedef struct {
 
 	gulong devtimeout_link_changed_handler;
 	guint devtimeout_timeout_id;
+
+	NMInotifyHelper *inotify_helper;
 } NMIfcfgConnectionPrivate;
 
 enum {
@@ -84,6 +86,14 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static NMInotifyHelper *
+_get_inotify_helper (NMIfcfgConnectionPrivate *priv)
+{
+	if (!priv->inotify_helper)
+		priv->inotify_helper = g_object_ref (nm_inotify_helper_get ());
+	return priv->inotify_helper;
+}
+
 static gboolean
 devtimeout_ready (gpointer user_data)
 {
@@ -149,6 +159,7 @@ nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
 	const char *ifname;
 	const char *filename;
 	guint devtimeout;
+	const NMPlatformLink *pllink;
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
 
@@ -160,11 +171,13 @@ nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
 	filename = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self));
 	if (!filename)
 		return;
-	devtimeout = devtimeout_from_file (filename);
-	if (!devtimeout)
+
+	pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname);
+	if (pllink && pllink->initialized)
 		return;
 
-	if (nm_platform_link_get_ifindex (NM_PLATFORM_GET, ifname) != 0)
+	devtimeout = devtimeout_from_file (filename);
+	if (!devtimeout)
 		return;
 
 	/* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */
@@ -261,7 +274,7 @@ path_watch_stop (NMIfcfgConnection *self)
 	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
 	NMInotifyHelper *ih;
 
-	ih = nm_inotify_helper_get ();
+	ih = _get_inotify_helper (priv);
 
 	if (priv->ih_event_id) {
 		g_signal_handler_disconnect (ih, priv->ih_event_id);
@@ -315,7 +328,9 @@ filename_changed (GObject *object,
 	priv->route6file = utils_get_route6_path (ifcfg_path);
 
 	if (nm_config_get_monitor_connection_files (nm_config_get ())) {
-		NMInotifyHelper *ih = nm_inotify_helper_get ();
+		NMInotifyHelper *ih;
+
+		ih = _get_inotify_helper (priv);
 
 		priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (files_changed_cb), self);
 		priv->file_wd = nm_inotify_helper_add_watch (ih, ifcfg_path);
@@ -509,6 +524,8 @@ dispose (GObject *object)
 		priv->devtimeout_timeout_id = 0;
 	}
 
+	g_clear_object (&priv->inotify_helper);
+
 	G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->dispose (object);
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index 1a3f791a..526cdafe 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -42,7 +42,7 @@
 #include <selinux/selinux.h>
 #endif
 
-#include <nm-setting-connection.h>
+#include "nm-setting-connection.h"
 
 #include "common.h"
 #include "nm-dbus-glib-types.h"
@@ -64,22 +64,15 @@
 #define DBUS_OBJECT_PATH "/com/redhat/ifcfgrh1"
 
 
-#define _LOG_DEFAULT_DOMAIN  LOGD_SETTINGS
-
-#define _LOG(level, domain, ...) \
+#define _NMLOG_DOMAIN  LOGD_SETTINGS
+#define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (domain), \
+        nm_log ((level), (_NMLOG_DOMAIN), \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 "ifcfg-rh: " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
     } G_STMT_END
 
-#define _LOGT(...)      _LOG (LOGL_TRACE, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGD(...)      _LOG (LOGL_DEBUG, _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGI(...)      _LOG (LOGL_INFO,  _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGW(...)      _LOG (LOGL_WARN,  _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-#define _LOGE(...)      _LOG (LOGL_ERR,   _LOG_DEFAULT_DOMAIN, __VA_ARGS__)
-
 #define ERR_GET_MSG(err) (((err) && (err)->message) ? (err)->message : "(unknown)")
 
 
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index ec874167..72123e66 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -250,6 +250,15 @@ make_connection_setting (const char *file,
 		g_free (value);
 	}
 
+	switch (svTrueValue (ifcfg, "CONNECTION_METERED", -1)) {
+	case TRUE:
+		g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_YES, NULL);
+		break;
+	case FALSE:
+		g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_NO, NULL);
+		break;
+	}
+
 	return NM_SETTING (s_con);
 }
 
@@ -3492,6 +3501,80 @@ wireless_connection_from_ifcfg (const char *file,
 	return connection;
 }
 
+static void
+parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, char *value)
+{
+	NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
+	gboolean use_password = FALSE;
+	char **words, **iter, *flag;
+
+	if (!value || !value[0])
+		return;
+
+	words = g_strsplit_set (value, " ", 0);
+	iter = words;
+
+	while (iter[0]) {
+		if (g_str_equal (iter[0], "wol") && iter[1] && *iter[1]) {
+			for (flag = iter[1]; *flag; flag++) {
+				switch (*flag) {
+				case 'p':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_PHY;
+					break;
+				case 'u':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST;
+					break;
+				case 'm':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST;
+					break;
+				case 'b':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST;
+					break;
+				case 'a':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_ARP;
+					break;
+				case 'g':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC;
+					break;
+				case 's':
+					use_password = TRUE;
+					break;
+				case 'd':
+					wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
+					use_password = FALSE;
+					break;
+				default:
+					PARSE_WARNING ("unrecognized Wake-on-LAN option '%c'", *flag);
+				}
+			}
+
+			if (!NM_FLAGS_HAS (wol_flags, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+				use_password = FALSE;
+
+			g_object_set (s_wired, NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, NULL);
+			iter += 2;
+			continue;
+		}
+
+		if (g_str_equal (iter[0], "sopass") && iter[1] && *iter[1]) {
+			if (use_password) {
+				if (nm_utils_hwaddr_valid (iter[1], ETH_ALEN))
+					g_object_set (s_wired, NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, iter[1], NULL);
+				else
+					PARSE_WARNING ("Wake-on-LAN password '%s' is invalid", iter[1]);
+			} else
+				PARSE_WARNING ("Wake-on-LAN password not expected");
+			iter += 2;
+			continue;
+		}
+
+		/* Silently skip unknown options */
+		iter++;
+	}
+
+	g_strfreev (words);
+}
+
 static NMSetting *
 make_wired_setting (shvarFile *ifcfg,
                     const char *file,
@@ -3627,6 +3710,10 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
+	value = svGetValue (ifcfg, "ETHTOOL_OPTS", FALSE);
+	parse_ethtool_options (ifcfg, s_wired, value);
+	g_free (value);
+
 	return (NMSetting *) s_wired;
 
 error:
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 4a5ca1d1..283aa826 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -303,11 +303,11 @@ svGetValueFull (shvarFile *s, const char *key, gboolean verbatim)
  * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
  * return <default> otherwise
  */
-gboolean
-svTrueValue (shvarFile *s, const char *key, gboolean def)
+gint
+svTrueValue (shvarFile *s, const char *key, gint def)
 {
 	char *tmp;
-	gboolean returnValue = def;
+	gint returnValue = def;
 
 	tmp = svGetValue (s, key, FALSE);
 	if (!tmp)
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index 4902541b..de7a3585 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -62,7 +62,7 @@ char *svGetValueFull (shvarFile *s, const char *key, gboolean verbatim);
  * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
  * return <def> otherwise
  */
-gboolean svTrueValue (shvarFile *s, const char *key, gboolean def);
+gint svTrueValue (shvarFile *s, const char *key, gint def);
 
 gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback);
 
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 a20a78d9..7b5aaf17 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am
@@ -123,7 +123,8 @@ EXTRA_DIST = \
 	ifcfg-test-team-master \
 	ifcfg-test-team-port \
 	ifcfg-test-team-port-empty-config \
-	ifcfg-test-vlan-trailing-spaces
+	ifcfg-test-vlan-trailing-spaces \
+	ifcfg-test-wired-wake-on-lan
 
 # make target dependencies can't have colons in their names, which ends up
 # meaning that we can't add the alias files to EXTRA_DIST
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 ef06c5ef..2435434d 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in
@@ -518,7 +518,8 @@ EXTRA_DIST = \
 	ifcfg-test-team-master \
 	ifcfg-test-team-port \
 	ifcfg-test-team-port-empty-config \
-	ifcfg-test-vlan-trailing-spaces
+	ifcfg-test-vlan-trailing-spaces \
+	ifcfg-test-wired-wake-on-lan
 
 
 # make target dependencies can't have colons in their names, which ends up
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan
new file mode 100644
index 00000000..1dfc9a43
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-wake-on-lan
@@ -0,0 +1,22 @@
+# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile)
+TYPE=Ethernet
+DEVICE=eth0
+HWADDR=00:11:22:33:44:ee
+BOOTPROTO=none
+ONBOOT=yes
+USERCTL=yes
+MTU=1492
+NM_CONTROLLED=yes
+DNS1=4.2.2.1
+DNS2=4.2.2.2
+IPADDR=192.168.1.5
+NETMASK=255.255.255.0
+GATEWAY=192.168.1.1
+IPV6INIT=yes
+IPV6_AUTOCONF=no
+IPV6ADDR=dead:beaf::1
+IPV6ADDR_SECONDARIES="dead:beaf::2/56"
+DNS3=1:2:3:4::a
+DNS4=1:2:3:4::b
+RES_OPTIONS=
+ETHTOOL_OPTS="speed 100 duplex full wol apgs sopass 00:11:22:33:44:55 autoneg off"
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 d2fb6867..f2f85d3c 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -5104,6 +5104,43 @@ test_read_wifi_wep_eap_ttls_chap (void)
 }
 
 static void
+test_read_wired_wake_on_lan (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	gboolean success;
+	GError *error = NULL;
+
+	connection = connection_from_file_test (TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-wake-on-lan",
+	                                        NULL, TYPE_WIRELESS, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert_cmpint (nm_setting_wired_get_wake_on_lan (s_wired),
+	                 ==,
+	                 NM_SETTING_WIRED_WAKE_ON_LAN_ARP |
+	                 NM_SETTING_WIRED_WAKE_ON_LAN_PHY |
+	                 NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC);
+
+	g_assert_cmpstr (nm_setting_wired_get_wake_on_lan_password (s_wired),
+	                 ==,
+	                 "00:11:22:33:44:55");
+
+	g_object_unref (connection);
+}
+
+static void
 test_read_wifi_hidden (void)
 {
 	NMConnection *connection;
@@ -5218,6 +5255,90 @@ test_write_wifi_hidden (void)
 }
 
 static void
+test_write_wired_wake_on_lan (void)
+{
+	NMConnection *connection, *reread;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingWiredWakeOnLan wol;
+	char *uuid, *testfile = NULL, *val;
+	gboolean success;
+	GError *error = NULL;
+	shvarFile *f;
+
+	connection = nm_simple_connection_new ();
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Write Wired Wake-on-LAN",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	wol = NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST |
+	      NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST |
+	      NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC;
+
+	g_object_set (s_wired,
+	              NM_SETTING_WIRED_WAKE_ON_LAN, wol,
+	              NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, "00:00:00:11:22:33",
+	              NULL);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Save the ifcfg */
+	success = writer_new_connection (connection,
+	                                 TEST_SCRATCH_DIR "/network-scripts/",
+	                                 &testfile,
+	                                 &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	f = svOpenFile (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (f);
+
+	/* re-read the file to check that the key was written. */
+	val = svGetValue (f, "ETHTOOL_OPTS", FALSE);
+	g_assert (val);
+	g_assert (strstr (val, "wol"));
+	g_assert (strstr (val, "sopass 00:00:00:11:22:33"));
+	g_free (val);
+	svCloseFile (f);
+
+	/* reread will be normalized, so we must normalize connection too. */
+	nm_connection_normalize (connection, NULL, NULL, NULL);
+
+	/* re-read the connection for comparison */
+	reread = connection_from_file_test (testfile, NULL, TYPE_ETHERNET,
+	                                    NULL, &error);
+	unlink (testfile);
+	g_assert_no_error (error);
+	g_assert (reread);
+
+	success = nm_connection_verify (reread, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	g_assert (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	g_free (testfile);
+	g_object_unref (connection);
+	g_object_unref (reread);
+}
+
+static void
 test_read_wifi_band_a (void)
 {
 	NMConnection *connection;
@@ -6405,7 +6526,7 @@ test_write_wired_static_ip6_only_gw (gconstpointer user_data)
 	g_assert (addr6);
 
 	/* assert that the gateway was written and reloaded as expected */
-	if (!gateway6 || !strcmp (gateway6, "::")) {
+	if (!gateway6) {
 		g_assert (nm_setting_ip_config_get_gateway (s_ip6) == NULL);
 		g_assert (written_ifcfg_gateway == NULL);
 	} else {
@@ -12551,6 +12672,7 @@ int main (int argc, char **argv)
 	test_read_vlan_only_vlan_id ();
 	test_read_vlan_only_device ();
 	g_test_add_func (TPATH "vlan/physdev", test_read_vlan_physdev);
+	g_test_add_func (TPATH "wired/read-wake-on-lan", test_read_wired_wake_on_lan);
 
 	test_write_wired_static ();
 	test_write_wired_static_ip6_only ();
@@ -12565,6 +12687,7 @@ int main (int argc, char **argv)
 	test_write_wired_8021x_tls (NM_SETTING_802_1X_CK_SCHEME_BLOB, NM_SETTING_SECRET_FLAG_NONE);
 	test_write_wired_aliases ();
 	g_test_add_func (TPATH "ipv4/write-static-addresses-GATEWAY", test_write_gateway);
+	g_test_add_func (TPATH "wired/write-wake-on-lan", test_write_wired_wake_on_lan);
 	test_write_wifi_open ();
 	test_write_wifi_open_hex_ssid ();
 	test_write_wifi_wep ();
diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c
index 52bf51d1..69b7d64a 100644
--- a/src/settings/plugins/ifcfg-rh/writer.c
+++ b/src/settings/plugins/ifcfg-rh/writer.c
@@ -42,6 +42,8 @@
 #include <nm-setting-team-port.h>
 #include "nm-core-internal.h"
 #include <nm-utils.h>
+#include "nm-core-internal.h"
+#include "nm-macros-internal.h"
 
 #include "nm-logging.h"
 #include "gsystem-local-alloc.h"
@@ -1048,6 +1050,8 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	const char *const *s390_subchannels;
 	GString *str;
 	const char * const *macaddr_blacklist;
+	NMSettingWiredWakeOnLan wol;
+	const char *wol_password;
 
 	s_wired = nm_connection_get_setting_wired (connection);
 	if (!s_wired) {
@@ -1131,6 +1135,37 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		g_string_free (str, TRUE);
 	}
 
+	wol = nm_setting_wired_get_wake_on_lan (s_wired);
+	wol_password = nm_setting_wired_get_wake_on_lan_password (s_wired);
+	if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)
+		svSetValue (ifcfg, "ETHTOOL_OPTS", NULL, FALSE);
+	else {
+		str = g_string_sized_new (30);
+		g_string_append (str, "wol ");
+
+		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY))
+			g_string_append (str, "p");
+		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST))
+			g_string_append (str, "u");
+		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST))
+			g_string_append (str, "m");
+		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST))
+			g_string_append (str, "b");
+		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP))
+			g_string_append (str, "a");
+		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+			g_string_append (str, "g");
+
+		if (!NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ALL))
+			g_string_append (str, "d");
+
+		if (wol_password && NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+			g_string_append_printf (str, "s sopass %s", wol_password);
+
+		svSetValue (ifcfg, "ETHTOOL_OPTS", str->str, FALSE);
+		g_string_free (str, TRUE);
+	}
+
 	svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE);
 
 	return TRUE;
@@ -1159,11 +1194,42 @@ vlan_priority_maplist_to_stringlist (NMSettingVlan *s_vlan, NMVlanPriorityMap ma
 }
 
 static gboolean
+write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg)
+{
+	NMSettingWired *s_wired;
+	gboolean has_wired = FALSE;
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired) {
+		const char *device_mac, *cloned_mac;
+		char *tmp;
+		guint32 mtu;
+
+		has_wired = TRUE;
+
+		device_mac = nm_setting_wired_get_mac_address (s_wired);
+		if (device_mac)
+			svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
+
+		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
+		if (cloned_mac)
+			svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
+
+		mtu = nm_setting_wired_get_mtu (s_wired);
+		if (mtu) {
+			tmp = g_strdup_printf ("%u", mtu);
+			svSetValue (ifcfg, "MTU", tmp, FALSE);
+			g_free (tmp);
+		}
+	}
+	return has_wired;
+}
+
+static gboolean
 write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
 {
 	NMSettingVlan *s_vlan;
 	NMSettingConnection *s_con;
-	NMSettingWired *s_wired;
 	char *tmp;
 	guint32 vlan_flags = 0;
 
@@ -1217,34 +1283,13 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 	svSetValue (ifcfg, "MACADDR", NULL, FALSE);
 	svSetValue (ifcfg, "MTU", NULL, FALSE);
 
-	s_wired = nm_connection_get_setting_wired (connection);
-	if (s_wired) {
-		const char *device_mac, *cloned_mac;
-		guint32 mtu;
-
-		*wired = TRUE;
-
-		device_mac = nm_setting_wired_get_mac_address (s_wired);
-		if (device_mac)
-			svSetValue (ifcfg, "HWADDR", device_mac, FALSE);
-
-		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		if (cloned_mac)
-			svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE);
-
-		mtu = nm_setting_wired_get_mtu (s_wired);
-		if (mtu) {
-			tmp = g_strdup_printf ("%u", mtu);
-			svSetValue (ifcfg, "MTU", tmp, FALSE);
-			g_free (tmp);
-		}
-	}
+	*wired = write_wired_for_virtual (connection, ifcfg);
 
 	return TRUE;
 }
 
 static gboolean
-write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
 {
 	NMSettingBond *s_bond;
 	const char *iface;
@@ -1292,11 +1337,13 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, GError **erro
 	svSetValue (ifcfg, "TYPE", TYPE_BOND, FALSE);
 	svSetValue (ifcfg, "BONDING_MASTER", "yes", FALSE);
 
+	*wired = write_wired_for_virtual (connection, ifcfg);
+
 	return TRUE;
 }
 
 static gboolean
-write_team_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
 {
 	NMSettingTeam *s_team;
 	const char *iface;
@@ -1321,6 +1368,8 @@ write_team_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	svSetValue (ifcfg, "TEAM_CONFIG", config, FALSE);
 	svSetValue (ifcfg, "DEVICETYPE", TYPE_TEAM, FALSE);
 
+	*wired = write_wired_for_virtual (connection, ifcfg);
+
 	return TRUE;
 }
 
@@ -1764,6 +1813,17 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		svSetValue (ifcfg, "GATEWAY_PING_TIMEOUT", tmp, FALSE);
 		g_free (tmp);
 	}
+
+	switch (nm_setting_connection_get_metered (s_con)) {
+	case NM_METERED_YES:
+		svSetValue (ifcfg, "CONNECTION_METERED", "yes", FALSE);
+		break;
+	case NM_METERED_NO:
+		svSetValue (ifcfg, "CONNECTION_METERED", "no", FALSE);
+		break;
+	default:
+		svSetValue (ifcfg, "CONNECTION_METERED", NULL, FALSE);
+	}
 }
 
 static gboolean
@@ -2564,10 +2624,10 @@ write_connection (NMConnection *connection,
 		if (!write_infiniband_setting (connection, ifcfg, error))
 			goto out;
 	} else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) {
-		if (!write_bonding_setting (connection, ifcfg, error))
+		if (!write_bonding_setting (connection, ifcfg, &wired, error))
 			goto out;
 	} else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) {
-		if (!write_team_setting (connection, ifcfg, error))
+		if (!write_team_setting (connection, ifcfg, &wired, error))
 			goto out;
 	} else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
 		if (!write_bridge_setting (connection, ifcfg, error))
diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
index b78a24d0..c4c725c1 100644
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -308,12 +308,32 @@ nm_supplicant_config_get_blobs (NMSupplicantConfig * self)
 	return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->blobs;
 }
 
-#define TWO_GHZ_FREQS  "2412,2417,2422,2427,2432,2437,2442,2447,2452,2457,2462,2467,2472,2484"
-#define FIVE_GHZ_FREQS "4915,4920,4925,4935,4940,4945,4960,4980,5035,5040,5045,5055,5060,5080," \
-                         "5170,5180,5190,5200,5210,5220,5230,5240,5260,5280,5300,5320,5500," \
-                         "5520,5540,5560,5580,5600,5620,5640,5660,5680,5700,5745,5765,5785," \
-                         "5805,5825"
-
+static const char *
+wifi_freqs_to_string (gboolean bg_band)
+{
+	static const char *str_2ghz = NULL;
+	static const char *str_5ghz = NULL;
+	const char *str;
+
+	str = bg_band ? str_2ghz : str_5ghz;
+
+	if (G_UNLIKELY (str == NULL)) {
+		GString *tmp;
+		const guint *freqs;
+		int i;
+
+		freqs = bg_band ? nm_utils_wifi_2ghz_freqs () : nm_utils_wifi_5ghz_freqs ();
+		tmp = g_string_sized_new (bg_band ? 70 : 225);
+		for (i = 0; freqs[i]; i++)
+			g_string_append_printf (tmp, i == 0 ? "%d" : " %d", freqs[i]);
+		str = g_string_free (tmp, FALSE);
+		if (bg_band)
+			str_2ghz = str;
+		else
+			str_5ghz = str;
+	}
+	return str;
+}
 
 gboolean
 nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
@@ -323,6 +343,7 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
 	NMSupplicantConfigPrivate *priv;
 	gboolean is_adhoc, is_ap;
 	const char *mode, *band;
+	guint32 channel;
 	GBytes *ssid;
 	const char *bssid;
 
@@ -393,22 +414,35 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self,
 	}
 
 	band = nm_setting_wireless_get_band (setting);
+	channel = nm_setting_wireless_get_channel (setting);
 	if (band) {
-		const char *freqs = NULL;
+		if (channel) {
+			guint32 freq;
+			char *str_freq;
+
+			freq = nm_utils_wifi_channel_to_freq (channel, band);
+			str_freq = g_strdup_printf ("%u", freq);
+			if (!nm_supplicant_config_add_option (self, "freq_list", str_freq, -1, FALSE)) {
+				g_free (str_freq);
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding frequency list to supplicant config.");
+				return FALSE;
+			}
+			g_free (str_freq);
+		} else {
+			const char *freqs = NULL;
 
-		if (!strcmp (band, "a"))
-			freqs = FIVE_GHZ_FREQS;
-		else if (!strcmp (band, "bg"))
-			freqs = TWO_GHZ_FREQS;
+			if (!strcmp (band, "a"))
+				freqs = wifi_freqs_to_string (FALSE);
+			else if (!strcmp (band, "bg"))
+				freqs = wifi_freqs_to_string (TRUE);
 
-		if (freqs && !nm_supplicant_config_add_option (self, "freq_list", freqs, strlen (freqs), FALSE)) {
-			nm_log_warn (LOGD_SUPPLICANT, "Error adding frequency list/band to supplicant config.");
-			return FALSE;
+			if (freqs && !nm_supplicant_config_add_option (self, "freq_list", freqs, strlen (freqs), FALSE)) {
+				nm_log_warn (LOGD_SUPPLICANT, "Error adding frequency list/band to supplicant config.");
+				return FALSE;
+			}
 		}
 	}
 
-	// FIXME: channel config item
-	
 	return TRUE;
 }
 
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index e9775a19..66bab2e3 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -1093,8 +1093,14 @@ scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
-	if (error)
-		nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", error->message);
+	if (error) {
+		if (_nm_dbus_error_has_name (error, "fi.w1.wpa_supplicant1.Interface.ScanError"))
+			nm_log_dbg (LOGD_SUPPLICANT, "Could not get scan request result: %s", error->message);
+		else {
+			g_dbus_error_strip_remote_error (error);
+			nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", error->message);
+		}
+	}
 	g_signal_emit (NM_SUPPLICANT_INTERFACE (user_data), signals[SCAN_DONE], 0, error ? FALSE : TRUE);
 }
 
diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c
index b5f65ded..681f4966 100644
--- a/src/tests/config/nm-test-device.c
+++ b/src/tests/config/nm-test-device.c
@@ -26,10 +26,10 @@
 #include "nm-device-private.h"
 #include "nm-utils.h"
 
-static GObjectClass *g_object_class;
-
 G_DEFINE_TYPE (NMTestDevice, nm_test_device, NM_TYPE_DEVICE)
 
+#define PARENT_CLASS (G_OBJECT_CLASS (g_type_class_peek_parent (nm_test_device_parent_class)))
+
 static void
 nm_test_device_init (NMTestDevice *self)
 {
@@ -44,21 +44,21 @@ constructor (GType type,
              guint n_construct_params,
              GObjectConstructParam *construct_params)
 {
-	return g_object_class->constructor (type,
-	                                    n_construct_params,
-	                                    construct_params);
+	return PARENT_CLASS->constructor (type,
+	                                  n_construct_params,
+	                                  construct_params);
 }
 
 static void
 constructed (GObject *object)
 {
-	g_object_class->constructed (object);
+	PARENT_CLASS->constructed (object);
 }
 
 static void
 dispose (GObject *object)
 {
-	g_object_class->dispose (object);
+	PARENT_CLASS->dispose (object);
 }
 
 static NMDeviceCapabilities
@@ -73,8 +73,6 @@ nm_test_device_class_init (NMTestDeviceClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	g_object_class = g_type_class_peek (G_TYPE_OBJECT);
-
 	object_class->constructor = constructor;
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index bb52bde2..fe76e3fa 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -35,6 +35,42 @@
 
 /*******************************************/
 
+static void
+test_nm_utils_monotonic_timestamp_as_boottime (void)
+{
+	gint64 timestamp_ns_per_tick, now, now_boottime, now_boottime_2, now_boottime_3;
+	struct timespec tp;
+	clockid_t clockid;
+	guint i;
+
+	if (clock_gettime (CLOCK_BOOTTIME, &tp) != 0 && errno == EINVAL)
+		clockid = CLOCK_MONOTONIC;
+	else
+		clockid = CLOCK_BOOTTIME;
+
+	for (i = 0; i < 10; i++) {
+
+		if (clock_gettime (clockid, &tp) != 0)
+			g_assert_not_reached ();
+		now_boottime = ( ((gint64) tp.tv_sec) * NM_UTILS_NS_PER_SECOND ) + ((gint64) tp.tv_nsec);
+
+		now = nm_utils_get_monotonic_timestamp_ns ();
+
+		now_boottime_2 = nm_utils_monotonic_timestamp_as_boottime (now, 1);
+		g_assert_cmpint (now_boottime_2, >=, 0);
+		g_assert_cmpint (now_boottime_2, >=, now_boottime);
+		g_assert_cmpint (now_boottime_2 - now_boottime, <=, NM_UTILS_NS_PER_SECOND / 1000);
+
+		for (timestamp_ns_per_tick = 1; timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND; timestamp_ns_per_tick *= 10) {
+			now_boottime_3 = nm_utils_monotonic_timestamp_as_boottime (now / timestamp_ns_per_tick, timestamp_ns_per_tick);
+
+			g_assert_cmpint (now_boottime_2 / timestamp_ns_per_tick, ==, now_boottime_3);
+		}
+	}
+}
+
+/*******************************************/
+
 struct test_nm_utils_kill_child_async_data
 {
 	GMainLoop *loop;
@@ -385,7 +421,7 @@ _remove_at_indexes_init_random_idx (GArray *idx, guint array_len, guint idx_len)
 }
 
 static void
-test_nm_utils_array_remove_at_indexes ()
+test_nm_utils_array_remove_at_indexes (void)
 {
 	gs_unref_array GArray *idx = NULL, *array = NULL;
 	gs_unref_hashtable GHashTable *unique = NULL;
@@ -822,6 +858,7 @@ main (int argc, char **argv)
 {
 	nmtst_init_assert_logging (&argc, &argv, "DEBUG", "DEFAULT");
 
+	g_test_add_func ("/general/nm_utils_monotonic_timestamp_as_boottime", test_nm_utils_monotonic_timestamp_as_boottime);
 	g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child);
 	g_test_add_func ("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid);
 	g_test_add_func ("/general/nm_multi_index", test_nm_multi_index);
diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c
index c8bdc75f..235cfd42 100644
--- a/src/tests/test-ip4-config.c
+++ b/src/tests/test-ip4-config.c
@@ -316,12 +316,12 @@ test_merge_subtract_mss_mtu (void)
 	nm_ip4_config_set_mss (cfg3, expected_mss3);
 	nm_ip4_config_set_mtu (cfg3, expected_mtu3, NM_IP_CONFIG_SOURCE_UNKNOWN);
 
-	nm_ip4_config_merge (cfg1, cfg2);
+	nm_ip4_config_merge (cfg1, cfg2, NM_IP_CONFIG_MERGE_DEFAULT);
 	/* ensure MSS and MTU are in cfg1 */
 	g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, expected_mss2);
 	g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu2);
 
-	nm_ip4_config_merge (cfg1, cfg3);
+	nm_ip4_config_merge (cfg1, cfg3, NM_IP_CONFIG_MERGE_DEFAULT);
 	/* ensure again the MSS and MTU in cfg1 got overriden */
 	g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, expected_mss3);
 	g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu3);
diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c
index 0164930d..9046bf88 100644
--- a/src/tests/test-route-manager.c
+++ b/src/tests/test-route-manager.c
@@ -377,7 +377,7 @@ setup_dev0_ip6 (int ifindex)
 	/* Add an address so that a route to the gateway below gets added. */
 	nm_platform_ip6_address_add (NM_PLATFORM_GET,
 	                             ifindex,
-	                             *nmtst_inet6_from_string ("2001:db8:8086::2"),
+	                             *nmtst_inet6_from_string ("2001:db8:8086::666"),
 	                             in6addr_any,
 	                             64,
 	                             3600,
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 795a8187..c34155ef 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -932,11 +932,11 @@ apply_parent_device_config (NMVpnConnection *connection)
 		 * vpn-config. Instead we tell NMDefaultRouteManager directly about the
 		 * default route. */
 		if (vpn4_parent_config) {
-			nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config);
+			nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 			nm_ip4_config_set_gateway (vpn4_parent_config, 0);
 		}
 		if (vpn6_parent_config) {
-			nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config);
+			nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
 			nm_ip6_config_set_gateway (vpn6_parent_config, NULL);
 		}
 	}