summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-08-28 01:13:48 +0200
committerMichael Biebl <biebl@debian.org>2015-08-28 01:13:48 +0200
commit81836c2d44802b4cca833d7775dd627e0797a7e2 (patch)
tree91154e6cefc0465306603f51ec0010d9963bbea4 /src/devices
parent50a58f0fabd8a34c1b6108a107e08abe3c1ccd24 (diff)
Imported Upstream version 1.0.6 upstream/1.0.6
Diffstat (limited to 'src/devices')
-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
11 files changed, 486 insertions, 159 deletions
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;