summary refs log tree commit diff
path: root/src/nm-policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-policy.c')
-rw-r--r--src/nm-policy.c637
1 files changed, 437 insertions, 200 deletions
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 38a03613..e8e7c606 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -43,7 +43,7 @@
 #include "nm-dispatcher.h"
 #include "nm-utils.h"
 
-struct NMPolicy {
+typedef struct {
 	NMManager *manager;
 	guint update_state_id;
 	GSList *pending_activation_checks;
@@ -58,8 +58,8 @@ struct NMPolicy {
 
 	NMSettings *settings;
 
-	NMDevice *default_device4;
-	NMDevice *default_device6;
+	NMDevice *default_device4, *activating_device4;
+	NMDevice *default_device6, *activating_device6;
 
 	HostnameThread *lookup;
 	guint32 lookup_ipv4_addr;          /* IPv4 for reverse lookup */
@@ -72,6 +72,19 @@ struct NMPolicy {
 	char *orig_hostname; /* hostname at NM start time */
 	char *cur_hostname;  /* hostname we want to assign */
 	gboolean hostname_changed;  /* TRUE if NM ever set the hostname */
+} NMPolicyPrivate;
+
+#define NM_POLICY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_POLICY, NMPolicyPrivate))
+
+G_DEFINE_TYPE (NMPolicy, nm_policy, G_TYPE_OBJECT)
+
+enum {
+	PROP_0,
+
+	PROP_DEFAULT_IP4_DEVICE,
+	PROP_DEFAULT_IP6_DEVICE,
+	PROP_ACTIVATING_IP4_DEVICE,
+	PROP_ACTIVATING_IP6_DEVICE
 };
 
 #define RETRIES_TAG "autoconnect-retries"
@@ -84,7 +97,7 @@ static void schedule_activate_all (NMPolicy *policy);
 
 
 static NMDevice *
-get_best_ip4_device (NMManager *manager)
+get_best_ip4_device (NMManager *manager, gboolean fully_activated)
 {
 	GSList *devices, *iter;
 	NMDevice *best = NULL;
@@ -97,6 +110,7 @@ get_best_ip4_device (NMManager *manager)
 	for (iter = devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *dev = NM_DEVICE (iter->data);
 		NMDeviceType devtype = nm_device_get_device_type (dev);
+		NMDeviceState state = nm_device_get_state (dev);
 		NMActRequest *req;
 		NMConnection *connection;
 		NMIP4Config *ip4_config;
@@ -106,12 +120,33 @@ get_best_ip4_device (NMManager *manager)
 		gboolean can_default = FALSE;
 		const char *method = NULL;
 
-		if (   nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED
-		    && nm_device_get_state (dev) != NM_DEVICE_STATE_SECONDARIES)
+		if (   state <= NM_DEVICE_STATE_DISCONNECTED
+		    || state >= NM_DEVICE_STATE_DEACTIVATING)
+			continue;
+
+		if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES)
 			continue;
 
 		ip4_config = nm_device_get_ip4_config (dev);
-		if (!ip4_config)
+		if (ip4_config) {
+			/* Make sure at least one of this device's IP addresses has a gateway */
+			for (i = 0; i < nm_ip4_config_get_num_addresses (ip4_config); i++) {
+				NMIP4Address *addr;
+
+				addr = nm_ip4_config_get_address (ip4_config, i);
+				if (nm_ip4_address_get_gateway (addr)) {
+					can_default = TRUE;
+					break;
+				}
+			}
+
+			if (!can_default && (devtype != NM_DEVICE_TYPE_MODEM))
+				continue;
+
+			/* 'never-default' devices can't ever be the default */
+			if (nm_ip4_config_get_never_default (ip4_config))
+				continue;
+		} else if (fully_activated)
 			continue;
 
 		req = nm_device_get_act_request (dev);
@@ -119,33 +154,18 @@ get_best_ip4_device (NMManager *manager)
 		connection = nm_act_request_get_connection (req);
 		g_assert (connection);
 
-		/* Never set the default route through an IPv4LL-addressed device */
 		s_ip4 = nm_connection_get_setting_ip4_config (connection);
-		if (s_ip4)
+		if (s_ip4) {
+			/* Never set the default route through an IPv4LL-addressed device */
 			method = nm_setting_ip4_config_get_method (s_ip4);
+			if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+				continue;
 
-		if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
-			continue;
-
-		/* Make sure at least one of this device's IP addresses has a gateway */
-		for (i = 0; i < nm_ip4_config_get_num_addresses (ip4_config); i++) {
-			NMIP4Address *addr;
-
-			addr = nm_ip4_config_get_address (ip4_config, i);
-			if (nm_ip4_address_get_gateway (addr)) {
-				can_default = TRUE;
-				break;
-			}
+			/* 'never-default' devices can't ever be the default */
+			if (nm_setting_ip4_config_get_never_default (s_ip4))
+				continue;
 		}
 
-		if (!can_default && (devtype != NM_DEVICE_TYPE_MODEM))
-			continue;
-
-		/* 'never-default' devices can't ever be the default */
-		if (   (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4))
-		    || nm_ip4_config_get_never_default (ip4_config))
-			continue;
-
 		prio = nm_device_get_priority (dev);
 		if (prio > 0 && prio < best_prio) {
 			best = dev;
@@ -153,11 +173,25 @@ get_best_ip4_device (NMManager *manager)
 		}
 	}
 
+	if (!best)
+		return NULL;
+
+	if (!fully_activated) {
+		NMDeviceState state = nm_device_get_state (best);
+
+		/* There's only a best activating device if the best device
+		 * among all activating and already-activated devices is a
+		 * still-activating one.
+		 */
+		if (state >= NM_DEVICE_STATE_SECONDARIES)
+			return NULL;
+	}
+
 	return best;
 }
 
 static NMDevice *
-get_best_ip6_device (NMManager *manager)
+get_best_ip6_device (NMManager *manager, gboolean fully_activated)
 {
 	GSList *devices, *iter;
 	NMDevice *best = NULL;
@@ -170,6 +204,7 @@ get_best_ip6_device (NMManager *manager)
 	for (iter = devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *dev = NM_DEVICE (iter->data);
 		NMDeviceType devtype = nm_device_get_device_type (dev);
+		NMDeviceState state = nm_device_get_state (dev);
 		NMActRequest *req;
 		NMConnection *connection;
 		NMIP6Config *ip6_config;
@@ -179,12 +214,31 @@ get_best_ip6_device (NMManager *manager)
 		gboolean can_default = FALSE;
 		const char *method = NULL;
 
-		if (   nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED
-		    && nm_device_get_state (dev) != NM_DEVICE_STATE_SECONDARIES)
+		if (   state <= NM_DEVICE_STATE_DISCONNECTED
+		    || state >= NM_DEVICE_STATE_DEACTIVATING)
+			continue;
+
+		if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES)
 			continue;
 
 		ip6_config = nm_device_get_ip6_config (dev);
-		if (!ip6_config)
+		if (ip6_config) {
+			for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
+				NMIP6Address *addr;
+
+				addr = nm_ip6_config_get_address (ip6_config, i);
+				if (nm_ip6_address_get_gateway (addr)) {
+					can_default = TRUE;
+					break;
+				}
+			}
+
+			if (!can_default && (devtype != NM_DEVICE_TYPE_MODEM))
+				continue;
+
+			if (nm_ip6_config_get_never_default (ip6_config))
+				continue;
+		} else if (fully_activated)
 			continue;
 
 		req = nm_device_get_act_request (dev);
@@ -192,32 +246,16 @@ get_best_ip6_device (NMManager *manager)
 		connection = nm_act_request_get_connection (req);
 		g_assert (connection);
 
-		/* Never set the default route through an IPv4LL-addressed device */
 		s_ip6 = nm_connection_get_setting_ip6_config (connection);
-		if (s_ip6)
+		if (s_ip6) {
 			method = nm_setting_ip6_config_get_method (s_ip6);
+			if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
+				continue;
 
-		if (method && !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
-			continue;
-
-		/* Make sure at least one of this device's IP addresses has a gateway */
-		for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) {
-			NMIP6Address *addr;
-
-			addr = nm_ip6_config_get_address (ip6_config, i);
-			if (nm_ip6_address_get_gateway (addr)) {
-				can_default = TRUE;
-				break;
-			}
+			if (nm_setting_ip6_config_get_never_default (s_ip6))
+				continue;
 		}
 
-		if (!can_default && (devtype != NM_DEVICE_TYPE_MODEM))
-			continue;
-
-		/* 'never-default' devices can't ever be the default */
-		if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6))
-			continue;
-
 		prio = nm_device_get_priority (dev);
 		if (prio > 0 && prio < best_prio) {
 			best = dev;
@@ -225,6 +263,20 @@ get_best_ip6_device (NMManager *manager)
 		}
 	}
 
+	if (!best)
+		return NULL;
+
+	if (!fully_activated) {
+		NMDeviceState state = nm_device_get_state (best);
+
+		/* There's only a best activating device if the best device
+		 * among all activating and already-activated devices is an
+		 * activating one.
+		 */
+		if (state >= NM_DEVICE_STATE_SECONDARIES)
+			return NULL;
+	}
+
 	return best;
 }
 
@@ -233,6 +285,8 @@ _set_hostname (NMPolicy *policy,
                const char *new_hostname,
                const char *msg)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+
 	/* The incoming hostname *can* be NULL, which will get translated to
 	 * 'localhost.localdomain' or such in the hostname policy code, but we
 	 * keep cur_hostname = NULL in the case because we need to know that
@@ -243,32 +297,32 @@ _set_hostname (NMPolicy *policy,
 	 * restart reverse lookup thread later.
 	 */
 	if (new_hostname) {
-		policy->lookup_ipv4_addr = 0;
-		g_free (policy->lookup_ipv6_addr);
-		policy->lookup_ipv6_addr = NULL;
+		priv->lookup_ipv4_addr = 0;
+		g_free (priv->lookup_ipv6_addr);
+		priv->lookup_ipv6_addr = NULL;
 	}
 
 	/* Don't change the hostname or update DNS this is the first time we're
 	 * trying to change the hostname, and it's not actually changing.
 	 */
-	if (   policy->orig_hostname
-	    && (policy->hostname_changed == FALSE)
-	    && g_strcmp0 (policy->orig_hostname, new_hostname) == 0)
+	if (   priv->orig_hostname
+	    && (priv->hostname_changed == FALSE)
+	    && g_strcmp0 (priv->orig_hostname, new_hostname) == 0)
 		return;
 
 	/* Don't change the hostname or update DNS if the hostname isn't actually
 	 * going to change.
 	 */
-	if (g_strcmp0 (policy->cur_hostname, new_hostname) == 0)
+	if (g_strcmp0 (priv->cur_hostname, new_hostname) == 0)
 		return;
 
-	g_free (policy->cur_hostname);
-	policy->cur_hostname = g_strdup (new_hostname);
-	policy->hostname_changed = TRUE;
+	g_free (priv->cur_hostname);
+	priv->cur_hostname = g_strdup (new_hostname);
+	priv->hostname_changed = TRUE;
 
-	nm_dns_manager_set_hostname (policy->dns_manager, policy->cur_hostname);
+	nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
 
-	if (nm_policy_set_system_hostname (policy->cur_hostname, msg))
+	if (nm_policy_set_system_hostname (priv->cur_hostname, msg))
 		nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL);
 }
 
@@ -279,11 +333,12 @@ lookup_callback (HostnameThread *thread,
                  gpointer user_data)
 {
 	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	char *msg;
 
 	/* Update the hostname if the calling lookup thread is the in-progress one */
-	if (!hostname_thread_is_dead (thread) && (thread == policy->lookup)) {
-		policy->lookup = NULL;
+	if (!hostname_thread_is_dead (thread) && (thread == priv->lookup)) {
+		priv->lookup = NULL;
 		if (!hostname) {
 			/* Fall back to localhost.localdomain */
 			msg = g_strdup_printf ("address lookup failed: %d", result);
@@ -298,14 +353,15 @@ lookup_callback (HostnameThread *thread,
 static void
 update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	char *configured_hostname = NULL;
 	const char *dhcp_hostname, *p;
 
 	g_return_if_fail (policy != NULL);
 
-	if (policy->lookup) {
-		hostname_thread_kill (policy->lookup);
-		policy->lookup = NULL;
+	if (priv->lookup) {
+		hostname_thread_kill (priv->lookup);
+		priv->lookup = NULL;
 	}
 
 	/* Hostname precedence order:
@@ -318,7 +374,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 	 */
 
 	/* Try a persistent hostname first */
-	g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
+	g_object_get (G_OBJECT (priv->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
 	if (configured_hostname) {
 		_set_hostname (policy, configured_hostname, "from system configuration");
 		g_free (configured_hostname);
@@ -327,15 +383,15 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 
 	/* Try automatically determined hostname from the best device's IP config */
 	if (!best4)
-		best4 = get_best_ip4_device (policy->manager);
+		best4 = get_best_ip4_device (priv->manager, TRUE);
 	if (!best6)
-		best6 = get_best_ip6_device (policy->manager);
+		best6 = get_best_ip6_device (priv->manager, TRUE);
 
 	if (!best4 && !best6) {
 		/* No best device; fall back to original hostname or if there wasn't
 		 * one, 'localhost.localdomain'
 		 */
-		_set_hostname (policy, policy->orig_hostname, "no default device");
+		_set_hostname (policy, priv->orig_hostname, "no default device");
 		return;
 	}
 
@@ -382,8 +438,8 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 	/* If no automatically-configured hostname, try using the hostname from
 	 * when NM started up.
 	 */
-	if (policy->orig_hostname) {
-		_set_hostname (policy, policy->orig_hostname, "from system startup");
+	if (priv->orig_hostname) {
+		_set_hostname (policy, priv->orig_hostname, "from system startup");
 		return;
 	}
 
@@ -407,8 +463,8 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 		g_assert (addr4); /* checked for > 1 address above */
 
 		/* Start the hostname lookup thread */
-		policy->lookup_ipv4_addr = nm_ip4_address_get_address (addr4);
-		policy->lookup = hostname4_thread_new (policy->lookup_ipv4_addr, lookup_callback, policy);
+		priv->lookup_ipv4_addr = nm_ip4_address_get_address (addr4);
+		priv->lookup = hostname4_thread_new (priv->lookup_ipv4_addr, lookup_callback, policy);
 	} else if (best6) {
 		NMIP6Config *ip6_config;
 		NMIP6Address *addr6;
@@ -426,12 +482,12 @@ update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
 		g_assert (addr6); /* checked for > 1 address above */
 
 		/* Start the hostname lookup thread */
-		policy->lookup_ipv6_addr = g_malloc0 (sizeof (struct in6_addr));
-		memcpy (policy->lookup_ipv6_addr, nm_ip6_address_get_address (addr6), sizeof (struct in6_addr));
-		policy->lookup = hostname6_thread_new (policy->lookup_ipv6_addr, lookup_callback, policy);
+		priv->lookup_ipv6_addr = g_malloc0 (sizeof (struct in6_addr));
+		memcpy (priv->lookup_ipv6_addr, nm_ip6_address_get_address (addr6), sizeof (struct in6_addr));
+		priv->lookup = hostname6_thread_new (priv->lookup_ipv6_addr, lookup_callback, policy);
 	}
 
-	if (!policy->lookup) {
+	if (!priv->lookup) {
 		/* Fall back to 'localhost.localdomain' */
 		_set_hostname (policy, NULL, "error starting hostname thread");
 	}
@@ -442,13 +498,14 @@ update_default_ac (NMPolicy *policy,
                    NMActiveConnection *best,
                    void (*set_active_func)(NMActiveConnection*, gboolean))
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	const GSList *connections, *iter;
 
 	/* Clear the 'default[6]' flag on all active connections that aren't the new
 	 * default active connection.  We'll set the new default after; this ensures
 	 * we don't ever have two marked 'default[6]' simultaneously.
 	 */
-	connections = nm_manager_get_active_connections (policy->manager);
+	connections = nm_manager_get_active_connections (priv->manager);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		if (NM_ACTIVE_CONNECTION (iter->data) != best)
 			set_active_func (NM_ACTIVE_CONNECTION (iter->data), FALSE);
@@ -468,13 +525,14 @@ get_best_ip4_config (NMPolicy *policy,
                      NMDevice **out_device,
                      NMVPNConnection **out_vpn)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	const GSList *connections, *iter;
 	NMDevice *device;
 	NMActRequest *req = NULL;
 	NMIP4Config *ip4_config = NULL;
 
 	/* If a VPN connection is active, it is preferred */
-	connections = nm_manager_get_active_connections (policy->manager);
+	connections = nm_manager_get_active_connections (priv->manager);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data);
 		NMVPNConnection *candidate;
@@ -524,7 +582,7 @@ get_best_ip4_config (NMPolicy *policy,
 
 	/* If no VPN connections, we use the best device instead */
 	if (!ip4_config) {
-		device = get_best_ip4_device (policy->manager);
+		device = get_best_ip4_device (priv->manager, TRUE);
 		if (device) {
 			ip4_config = nm_device_get_ip4_config (device);
 			g_assert (ip4_config);
@@ -568,6 +626,7 @@ update_ip4_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
 static void
 update_ip4_routing (NMPolicy *policy, gboolean force_update)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMDevice *best = NULL, *parent;
 	NMConnection *connection = NULL;
 	NMVPNConnection *vpn = NULL;
@@ -583,12 +642,18 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
 	 */
 	ip4_config = get_best_ip4_config (policy, FALSE, &ip_iface, &ip_ifindex, &best_ac, &best, &vpn);
 	if (!ip4_config) {
-		policy->default_device4 = NULL;
+		gboolean changed;
+
+		changed = (priv->default_device4 != NULL);
+		priv->default_device4 = NULL;
+		if (changed)
+			g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE);
+
 		return;
 	}
 	g_assert ((best || vpn) && best_ac);
 
-	if (!force_update && best && (best == policy->default_device4))
+	if (!force_update && best && (best == priv->default_device4))
 		return;
 
 	/* We set the default route to the first gateway we find.  If we don't find
@@ -622,11 +687,12 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
 	}
 
 	update_default_ac (policy, best_ac, nm_active_connection_set_default);
-	policy->default_device4 = best;
+	priv->default_device4 = best;
 
 	connection = nm_active_connection_get_connection (best_ac);
 	nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.",
 	             nm_connection_get_id (connection), ip_iface);
+	g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE);
 }
 
 static NMIP6Config *
@@ -638,13 +704,14 @@ get_best_ip6_config (NMPolicy *policy,
                      NMDevice **out_device,
                      NMVPNConnection **out_vpn)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	const GSList *connections, *iter;
 	NMDevice *device;
 	NMActRequest *req = NULL;
 	NMIP6Config *ip6_config = NULL;
 
 	/* If a VPN connection is active, it is preferred */
-	connections = nm_manager_get_active_connections (policy->manager);
+	connections = nm_manager_get_active_connections (priv->manager);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data);
 		NMVPNConnection *candidate;
@@ -694,7 +761,7 @@ get_best_ip6_config (NMPolicy *policy,
 
 	/* If no VPN connections, we use the best device instead */
 	if (!ip6_config) {
-		device = get_best_ip6_device (policy->manager);
+		device = get_best_ip6_device (priv->manager, TRUE);
 		if (device) {
 			req = nm_device_get_act_request (device);
 			g_assert (req);
@@ -738,6 +805,7 @@ update_ip6_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
 static void
 update_ip6_routing (NMPolicy *policy, gboolean force_update)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMDevice *best = NULL, *parent;
 	NMConnection *connection = NULL;
 	NMVPNConnection *vpn = NULL;
@@ -754,12 +822,18 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
 	 */
 	ip6_config = get_best_ip6_config (policy, FALSE, &ip_iface, &ip_ifindex, &best_ac, &best, &vpn);
 	if (!ip6_config) {
-		policy->default_device6 = NULL;
+		gboolean changed;
+
+		changed = (priv->default_device6 != NULL);
+		priv->default_device6 = NULL;
+		if (changed)
+			g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE);
+
 		return;
 	}
 	g_assert ((best || vpn) && best_ac);
 
-	if (!force_update && best && (best == policy->default_device6))
+	if (!force_update && best && (best == priv->default_device6))
 		return;
 
 	/* If no better gateway is found, use ::; not all configurations will
@@ -802,16 +876,18 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
 	}
 
 	update_default_ac (policy, best_ac, nm_active_connection_set_default6);
-	policy->default_device6 = best;
+	priv->default_device6 = best;
 
 	connection = nm_active_connection_get_connection (best_ac);
 	nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.",
 	             nm_connection_get_id (connection), ip_iface);
+	g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE);
 }
 
 static void
 update_routing_and_dns (NMPolicy *policy, gboolean force_update)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMDnsManager *mgr;
 
 	mgr = nm_dns_manager_get (NULL);
@@ -824,13 +900,37 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update)
 	update_ip6_routing (policy, force_update);
 
 	/* Update the system hostname */
-	update_system_hostname (policy, policy->default_device4, policy->default_device6);
+	update_system_hostname (policy, priv->default_device4, priv->default_device6);
 
 	nm_dns_manager_end_updates (mgr, __func__);
 	g_object_unref (mgr);
 }
 
 static void
+check_activating_devices (NMPolicy *policy)
+{
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	GObject *object = G_OBJECT (policy);
+	NMDevice *best4, *best6 = NULL;
+
+	best4 = get_best_ip4_device (priv->manager, FALSE);
+	best6 = get_best_ip6_device (priv->manager, FALSE);
+
+	g_object_freeze_notify (object);
+
+	if (best4 != priv->activating_device4) {
+		priv->activating_device4 = best4;
+		g_object_notify (object, NM_POLICY_ACTIVATING_IP4_DEVICE);
+	}
+	if (best6 != priv->activating_device6) {
+		priv->activating_device6 = best6;
+		g_object_notify (object, NM_POLICY_ACTIVATING_IP6_DEVICE);
+	}
+
+	g_object_thaw_notify (object);
+}
+
+static void
 set_connection_auto_retries (NMConnection *connection, guint retries)
 {
 	/* add +1 so that the tag still exists if the # retries is 0 */
@@ -865,15 +965,17 @@ auto_activate_device (gpointer user_data)
 {
 	ActivateData *data = (ActivateData *) user_data;
 	NMPolicy *policy;
+	NMPolicyPrivate *priv;
 	NMConnection *best_connection;
 	char *specific_object = NULL;
 	GSList *connections, *iter;
 
 	g_assert (data);
 	policy = data->policy;
+	priv = NM_POLICY_GET_PRIVATE (policy);
 
 	data->id = 0;
-	policy->pending_activation_checks = g_slist_remove (policy->pending_activation_checks, data);
+	priv->pending_activation_checks = g_slist_remove (priv->pending_activation_checks, data);
 
 	// FIXME: if a device is already activating (or activated) with a connection
 	// but another connection now overrides the current one for that device,
@@ -882,7 +984,7 @@ auto_activate_device (gpointer user_data)
 	if (nm_device_get_act_request (data->device))
 		goto out;
 
-	iter = connections = nm_settings_get_connections (policy->settings);
+	iter = connections = nm_settings_get_connections (priv->settings);
 
 	/* Remove connections that shouldn't be auto-activated */
 	while (iter) {
@@ -918,7 +1020,7 @@ auto_activate_device (gpointer user_data)
 
 		nm_log_info (LOGD_DEVICE, "Auto-activating connection '%s'.",
 		             nm_connection_get_id (best_connection));
-		if (!nm_manager_activate_connection (policy->manager,
+		if (!nm_manager_activate_connection (priv->manager,
 		                                     best_connection,
 		                                     specific_object,
 		                                     nm_device_get_path (data->device),
@@ -998,6 +1100,7 @@ process_secondaries (NMPolicy *policy,
                      NMActiveConnection *active,
                      gboolean connected)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMDevice *device = NULL;
 	const char *ac_path;
 	GSList *iter, *iter2;
@@ -1012,7 +1115,7 @@ process_secondaries (NMPolicy *policy,
 	if (NM_IS_VPN_CONNECTION (active))
 		device = nm_vpn_connection_get_parent_device (NM_VPN_CONNECTION (active));
 
-	for (iter = policy->pending_secondaries; iter; iter = g_slist_next (iter)) {
+	for (iter = priv->pending_secondaries; iter; iter = g_slist_next (iter)) {
 		PendingSecondaryData *secondary_data = (PendingSecondaryData *) iter->data;
 		NMDevice *item_device = secondary_data->device;
 
@@ -1027,14 +1130,14 @@ process_secondaries (NMPolicy *policy,
 						g_free (list_ac_path);
 						if (!secondary_data->secondaries) {
 							/* None secondary UUID remained -> remove the secondary data item */
-							policy->pending_secondaries = g_slist_remove (policy->pending_secondaries, secondary_data);
+							priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data);
 							pending_secondary_data_free (secondary_data);
 							nm_device_state_changed (item_device, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
 							return;
 						}
 					} else {
 						/* Secondary connection failed -> do not watch other connections */
-						policy->pending_secondaries = g_slist_remove (policy->pending_secondaries, secondary_data);
+						priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data);
 						pending_secondary_data_free (secondary_data);
 						nm_device_state_changed (item_device, NM_DEVICE_STATE_FAILED,
 						                                      NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED);
@@ -1094,6 +1197,7 @@ static void
 sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 {
 	NMPolicy *policy = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	gboolean sleeping = FALSE, enabled = FALSE;
 
 	g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL);
@@ -1101,16 +1205,17 @@ sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 
 	/* Reset retries on all connections so they'll checked on wakeup */
 	if (sleeping || !enabled)
-		reset_retries_all (policy->settings, NULL);
+		reset_retries_all (priv->settings, NULL);
 }
 
 static void
 schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	ActivateData *data;
 	NMDeviceState state;
 
-	if (nm_manager_get_state (policy->manager) == NM_STATE_ASLEEP)
+	if (nm_manager_get_state (priv->manager) == NM_STATE_ASLEEP)
 		return;
 
 	state = nm_device_get_state (device);
@@ -1124,9 +1229,9 @@ schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds
 		return;
 
 	/* Schedule an auto-activation if there isn't one already for this device */
-	if (find_pending_activation (policy->pending_activation_checks, device) == NULL) {
+	if (find_pending_activation (priv->pending_activation_checks, device) == NULL) {
 		data = activate_data_new (policy, device, delay_seconds);
-		policy->pending_activation_checks = g_slist_append (policy->pending_activation_checks, data);
+		priv->pending_activation_checks = g_slist_append (priv->pending_activation_checks, data);
 	}
 }
 
@@ -1134,14 +1239,15 @@ static gboolean
 reset_connections_retries (gpointer user_data)
 {
 	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	GSList *connections, *iter;
 	time_t con_stamp, min_stamp, now;
 	gboolean changed = FALSE;
 
-	policy->reset_retries_id = 0;
+	priv->reset_retries_id = 0;
 
 	min_stamp = now = time (NULL);
-	connections = nm_settings_get_connections (policy->settings);
+	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		con_stamp = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG));
 		if (con_stamp == 0)
@@ -1159,7 +1265,7 @@ reset_connections_retries (gpointer user_data)
 
 	/* Schedule the handler again if there are some stamps left */
 	if (min_stamp != now)
-		policy->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER - (now - min_stamp), reset_connections_retries, policy);
+		priv->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER - (now - min_stamp), reset_connections_retries, policy);
 
 	/* If anything changed, try to activate the newly re-enabled connections */
 	if (changed)
@@ -1174,13 +1280,14 @@ static void
 activate_slave_connections (NMPolicy *policy, NMConnection *connection,
                             NMDevice *device)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	const char *master_device;
 	GSList *connections, *iter;
 
 	master_device = nm_device_get_iface (device);
 	g_assert (master_device);
 
-	connections = nm_settings_get_connections (policy->settings);
+	connections = nm_settings_get_connections (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *slave;
 		NMSettingConnection *s_slave_con;
@@ -1205,6 +1312,7 @@ activate_secondary_connections (NMPolicy *policy,
                                 NMConnection *connection,
                                 NMDevice *device)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMSettingConnection *s_con;
 	NMSettingsConnection *settings_con;
 	NMActiveConnection *ac;
@@ -1220,7 +1328,7 @@ activate_secondary_connections (NMPolicy *policy,
 	for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) {
 		const char *sec_uuid = nm_setting_connection_get_secondary (s_con, i);
 
-		settings_con = nm_settings_get_connection_by_uuid (policy->settings, sec_uuid);
+		settings_con = nm_settings_get_connection_by_uuid (priv->settings, sec_uuid);
 		if (settings_con) {
 			NMActRequest *req = nm_device_get_act_request (device);
 			g_assert (req);
@@ -1228,7 +1336,7 @@ activate_secondary_connections (NMPolicy *policy,
 			nm_log_dbg (LOGD_DEVICE, "Activating secondary connection '%s (%s)' for base connection '%s (%s)'",
 			            nm_connection_get_id (NM_CONNECTION (settings_con)), sec_uuid,
 			            nm_connection_get_id (connection), nm_connection_get_uuid (connection));
-			ac = nm_manager_activate_connection (policy->manager,
+			ac = nm_manager_activate_connection (priv->manager,
 			                                     NM_CONNECTION (settings_con),
 			                                     nm_active_connection_get_path (NM_ACTIVE_CONNECTION (req)),
 			                                     nm_device_get_path (device),
@@ -1256,7 +1364,7 @@ activate_secondary_connections (NMPolicy *policy,
 
 	if (success && secondary_ac_list != NULL) {
 		secondary_data = pending_secondary_data_new (device, secondary_ac_list);
-		policy->pending_secondaries = g_slist_append (policy->pending_secondaries, secondary_data);
+		priv->pending_secondaries = g_slist_append (priv->pending_secondaries, secondary_data);
 	} else
 		nm_utils_slist_free (secondary_ac_list, g_free);
 
@@ -1271,6 +1379,7 @@ device_state_changed (NMDevice *device,
                       gpointer user_data)
 {
 	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMConnection *connection = nm_device_get_connection (device);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMIP4Config *ip4_config;
@@ -1313,8 +1422,8 @@ device_state_changed (NMDevice *device,
 				nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", nm_connection_get_id (connection));
 				/* Schedule a handler to reset retries count */
 				g_object_set_data (G_OBJECT (connection), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER ((gsize) time (NULL)));
-				if (!policy->reset_retries_id)
-					policy->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER, reset_connections_retries, policy);
+				if (!priv->reset_retries_id)
+					priv->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER, reset_connections_retries, policy);
 			}
 			nm_connection_clear_secrets (connection);
 		}
@@ -1333,18 +1442,18 @@ device_state_changed (NMDevice *device,
 
 		/* Add device's new IPv4 and IPv6 configs to DNS */
 
-		nm_dns_manager_begin_updates (policy->dns_manager, __func__);
+		nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
 		ip4_config = nm_device_get_ip4_config (device);
 		if (ip4_config)
-			nm_dns_manager_add_ip4_config (policy->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+			nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		ip6_config = nm_device_get_ip6_config (device);
 		if (ip6_config)
-			nm_dns_manager_add_ip6_config (policy->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+			nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 
 		update_routing_and_dns (policy, FALSE);
 
-		nm_dns_manager_end_updates (policy->dns_manager, __func__);
+		nm_dns_manager_end_updates (priv->dns_manager, __func__);
 		break;
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
@@ -1355,7 +1464,7 @@ device_state_changed (NMDevice *device,
 		/* Reset RETRIES_TAG when carrier on. If cable was unplugged
 		 * and plugged again, we should try to reconnect */
 		if (reason == NM_DEVICE_STATE_REASON_CARRIER && old_state == NM_DEVICE_STATE_UNAVAILABLE)
-			reset_retries_all (policy->settings, device);
+			reset_retries_all (priv->settings, device);
 
 		if (old_state > NM_DEVICE_STATE_DISCONNECTED)
 			update_routing_and_dns (policy, FALSE);
@@ -1387,6 +1496,8 @@ device_state_changed (NMDevice *device,
 	default:
 		break;
 	}
+
+	check_activating_devices (policy);
 }
 
 static void
@@ -1396,14 +1507,15 @@ device_ip4_config_changed (NMDevice *device,
                            gpointer user_data)
 {
 	NMPolicy *policy = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL;
 
-	nm_dns_manager_begin_updates (policy->dns_manager, __func__);
+	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
 	/* Old configs get removed immediately */
 	if (old_config)
-		nm_dns_manager_remove_ip4_config (policy->dns_manager, old_config);
+		nm_dns_manager_remove_ip4_config (priv->dns_manager, old_config);
 
 	/* Ignore IP config changes while the device is activating, because we'll
 	 * catch all the changes when the device moves to ACTIVATED state.
@@ -1411,8 +1523,8 @@ device_ip4_config_changed (NMDevice *device,
 	 */
 	if (!nm_device_is_activating (device)) {
 		if (new_config)
-			nm_dns_manager_add_ip4_config (policy->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
-		update_ip4_dns (policy, policy->dns_manager);
+			nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+		update_ip4_dns (policy, priv->dns_manager);
 
 		/* Only change routing if something actually changed */
 		diff = nm_ip4_config_diff (new_config, old_config);
@@ -1420,7 +1532,7 @@ device_ip4_config_changed (NMDevice *device,
 			update_ip4_routing (policy, TRUE);
 	}
 
-	nm_dns_manager_end_updates (policy->dns_manager, __func__);
+	nm_dns_manager_end_updates (priv->dns_manager, __func__);
 }
 
 static void
@@ -1430,14 +1542,15 @@ device_ip6_config_changed (NMDevice *device,
                            gpointer user_data)
 {
 	NMPolicy *policy = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL;
 
-	nm_dns_manager_begin_updates (policy->dns_manager, __func__);
+	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
 	/* Old configs get removed immediately */
 	if (old_config)
-		nm_dns_manager_remove_ip6_config (policy->dns_manager, old_config);
+		nm_dns_manager_remove_ip6_config (priv->dns_manager, old_config);
 
 	/* Ignore IP config changes while the device is activating, because we'll
 	 * catch all the changes when the device moves to ACTIVATED state.
@@ -1445,8 +1558,8 @@ device_ip6_config_changed (NMDevice *device,
 	 */
 	if (!nm_device_is_activating (device)) {
 		if (new_config)
-			nm_dns_manager_add_ip6_config (policy->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
-		update_ip6_dns (policy, policy->dns_manager);
+			nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+		update_ip6_dns (policy, priv->dns_manager);
 
 		/* Only change routing if something actually changed */
 		diff = nm_ip6_config_diff (new_config, old_config);
@@ -1454,7 +1567,7 @@ device_ip6_config_changed (NMDevice *device,
 			update_ip6_routing (policy, TRUE);
 	}
 
-	nm_dns_manager_end_updates (policy->dns_manager, __func__);
+	nm_dns_manager_end_updates (priv->dns_manager, __func__);
 }
 
 static void
@@ -1492,13 +1605,14 @@ typedef struct {
 static void
 _connect_device_signal (NMPolicy *policy, NMDevice *device, const char *name, gpointer callback)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	DeviceSignalId *data;
 
 	data = g_slice_new0 (DeviceSignalId);
 	g_assert (data);
 	data->id = g_signal_connect (device, name, callback, policy);
 	data->device = device;
-	policy->dev_ids = g_slist_prepend (policy->dev_ids, data);
+	priv->dev_ids = g_slist_prepend (priv->dev_ids, data);
 }
 
 static void
@@ -1532,18 +1646,19 @@ static void
 device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
 {
 	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	ActivateData *tmp;
 	GSList *iter;
 
 	/* Clear any idle callbacks for this device */
-	tmp = find_pending_activation (policy->pending_activation_checks, device);
+	tmp = find_pending_activation (priv->pending_activation_checks, device);
 	if (tmp) {
-		policy->pending_activation_checks = g_slist_remove (policy->pending_activation_checks, tmp);
+		priv->pending_activation_checks = g_slist_remove (priv->pending_activation_checks, tmp);
 		activate_data_free (tmp);
 	}
 
 	/* Clear any signal handlers for this device */
-	iter = policy->dev_ids;
+	iter = priv->dev_ids;
 	while (iter) {
 		DeviceSignalId *data = iter->data;
 		GSList *next = g_slist_next (iter);
@@ -1551,7 +1666,7 @@ device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
 		if (data->device == device) {
 			g_signal_handler_disconnect (data->device, data->id);
 			g_slice_free (DeviceSignalId, data);
-			policy->dev_ids = g_slist_delete_link (policy->dev_ids, iter);
+			priv->dev_ids = g_slist_delete_link (priv->dev_ids, iter);
 		}
 		iter = next;
 	}
@@ -1703,9 +1818,10 @@ active_connection_removed (NMManager *manager,
 static void
 schedule_activate_all (NMPolicy *policy)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	GSList *iter, *devices;
 
-	devices = nm_manager_get_devices (policy->manager);
+	devices = nm_manager_get_devices (priv->manager);
 	for (iter = devices; iter; iter = g_slist_next (iter))
 		schedule_activate_check (policy, NM_DEVICE (iter->data), 0);
 }
@@ -1747,17 +1863,18 @@ add_or_change_zone_cb (GError *error, gpointer user_data)
 static void
 firewall_update_zone (NMPolicy *policy, NMConnection *connection)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
 	GSList *iter, *devices;
 
-	devices = nm_manager_get_devices (policy->manager);
+	devices = nm_manager_get_devices (priv->manager);
 	/* find dev with passed connection and change zone its interface belongs to */
 	for (iter = devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *dev = NM_DEVICE (iter->data);
 
 		if (   (nm_device_get_connection (dev) == connection)
 		    && (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED)) {
-			nm_firewall_manager_add_or_change_zone (policy->fw_manager,
+			nm_firewall_manager_add_or_change_zone (priv->fw_manager,
 			                                        nm_device_get_ip_iface (dev),
 			                                        nm_setting_connection_get_zone (s_con),
 			                                        FALSE, /* change zone */
@@ -1772,11 +1889,12 @@ firewall_started (NMFirewallManager *manager,
                   gpointer user_data)
 {
 	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	NMConnection *connection;
 	NMSettingConnection *s_con;
 	GSList *iter, *devices;
 
-	devices = nm_manager_get_devices (policy->manager);
+	devices = nm_manager_get_devices (priv->manager);
 	/* add interface of each device to correct zone */
 	for (iter = devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *dev = NM_DEVICE (iter->data);
@@ -1784,7 +1902,7 @@ firewall_started (NMFirewallManager *manager,
 		connection = nm_device_get_connection (dev);
 		s_con = nm_connection_get_setting_connection (connection);
 		if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) {
-			nm_firewall_manager_add_or_change_zone (policy->fw_manager,
+			nm_firewall_manager_add_or_change_zone (priv->fw_manager,
 			                                        nm_device_get_ip_iface (dev),
 			                                        nm_setting_connection_get_zone (s_con),
 			                                        TRUE, /* add zone */
@@ -1798,6 +1916,7 @@ static void
 dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
 {
 	NMPolicy *policy = (NMPolicy *) user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 
 	/* Restart a thread for reverse-DNS lookup after we are signalled that
 	 * DNS changed. Because the result from a previous run may not be right
@@ -1805,29 +1924,29 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
 	 */
 
 	/* Stop a lookup thread if any. */
-	if (policy->lookup) {
-		hostname_thread_kill (policy->lookup);
-		policy->lookup = NULL;
+	if (priv->lookup) {
+		hostname_thread_kill (priv->lookup);
+		priv->lookup = NULL;
 	}
 
 	/* Re-start the hostname lookup thread if we don't have hostname yet. */
-	if (policy->lookup_ipv4_addr) {
+	if (priv->lookup_ipv4_addr) {
 		char buf[INET_ADDRSTRLEN];
-		struct in_addr addr = { .s_addr = policy->lookup_ipv4_addr };
+		struct in_addr addr = { .s_addr = priv->lookup_ipv4_addr };
 		
 		if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf)))
 			strcpy (buf, "(unknown)");
 		nm_log_dbg (LOGD_DNS, "restarting IPv4 reverse-lookup thread for address %s'", buf);
 
-		policy->lookup = hostname4_thread_new (policy->lookup_ipv4_addr, lookup_callback, policy);
-	} else if (policy->lookup_ipv6_addr) {
+		priv->lookup = hostname4_thread_new (priv->lookup_ipv4_addr, lookup_callback, policy);
+	} else if (priv->lookup_ipv6_addr) {
 		char buf[INET6_ADDRSTRLEN];
 
-		if (!inet_ntop (AF_INET6, policy->lookup_ipv6_addr, buf, sizeof (buf)))
+		if (!inet_ntop (AF_INET6, priv->lookup_ipv6_addr, buf, sizeof (buf)))
 			strcpy (buf, "(unknown)");
 		nm_log_dbg (LOGD_DNS, "restarting IPv6 reverse-lookup thread for address %s'", buf);
 
-		policy->lookup = hostname6_thread_new (policy->lookup_ipv6_addr, lookup_callback, policy);
+		priv->lookup = hostname6_thread_new (priv->lookup_ipv6_addr, lookup_callback, policy);
 	}
 }
 
@@ -1854,9 +1973,11 @@ _deactivate_if_active (NMManager *manager, NMConnection *connection)
 	active = nm_manager_get_active_connections (manager);
 	for (iter = active; iter; iter = g_slist_next (iter)) {
 		NMActiveConnection *ac = iter->data;
+		NMActiveConnectionState state = nm_active_connection_get_state (ac);
 		GError *error = NULL;
 
-		if (nm_active_connection_get_connection (ac) == connection) {
+		if (nm_active_connection_get_connection (ac) == connection &&
+		    (state <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
 			if (!nm_manager_deactivate_connection (manager,
 			                                       nm_active_connection_get_path (ac),
 			                                       NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
@@ -1877,8 +1998,9 @@ connection_removed (NMSettings *settings,
                     gpointer user_data)
 {
 	NMPolicy *policy = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 
-	_deactivate_if_active (policy->manager, connection);
+	_deactivate_if_active (priv->manager, connection);
 }
 
 static void
@@ -1887,11 +2009,12 @@ connection_visibility_changed (NMSettings *settings,
                                gpointer user_data)
 {
 	NMPolicy *policy = user_data;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 
 	if (nm_settings_connection_is_visible (connection))
 		schedule_activate_all (policy);
 	else
-		_deactivate_if_active (policy->manager, NM_CONNECTION (connection));
+		_deactivate_if_active (priv->manager, NM_CONNECTION (connection));
 }
 
 static void
@@ -1910,25 +2033,28 @@ secret_agent_registered (NMSettings *settings,
 static void
 _connect_manager_signal (NMPolicy *policy, const char *name, gpointer callback)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	guint id;
 
-	id = g_signal_connect (policy->manager, name, callback, policy);
-	policy->manager_ids = g_slist_prepend (policy->manager_ids, GUINT_TO_POINTER (id));
+	id = g_signal_connect (priv->manager, name, callback, policy);
+	priv->manager_ids = g_slist_prepend (priv->manager_ids, GUINT_TO_POINTER (id));
 }
 
 static void
 _connect_settings_signal (NMPolicy *policy, const char *name, gpointer callback)
 {
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
 	guint id;
 
-	id = g_signal_connect (policy->settings, name, callback, policy);
-	policy->settings_ids = g_slist_prepend (policy->settings_ids, GUINT_TO_POINTER (id));
+	id = g_signal_connect (priv->settings, name, callback, policy);
+	priv->settings_ids = g_slist_prepend (priv->settings_ids, GUINT_TO_POINTER (id));
 }
 
 NMPolicy *
 nm_policy_new (NMManager *manager, NMSettings *settings)
 {
 	NMPolicy *policy;
+	NMPolicyPrivate *priv;
 	static gboolean initialized = FALSE;
 	gulong id;
 	char hostname[HOST_NAME_MAX + 2];
@@ -1936,10 +2062,11 @@ nm_policy_new (NMManager *manager, NMSettings *settings)
 	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
 	g_return_val_if_fail (initialized == FALSE, NULL);
 
-	policy = g_malloc0 (sizeof (NMPolicy));
-	policy->manager = g_object_ref (manager);
-	policy->settings = g_object_ref (settings);
-	policy->update_state_id = 0;
+	policy = g_object_new (NM_TYPE_POLICY, NULL);
+	priv = NM_POLICY_GET_PRIVATE (policy);
+	priv->manager = manager;
+	priv->settings = g_object_ref (settings);
+	priv->update_state_id = 0;
 
 	/* Grab hostname on startup and use that if nothing provides one */
 	memset (hostname, 0, sizeof (hostname));
@@ -1949,16 +2076,16 @@ nm_policy_new (NMManager *manager, NMSettings *settings)
 		    && strcmp (hostname, "localhost")
 		    && strcmp (hostname, "localhost.localdomain")
 		    && strcmp (hostname, "(none)"))
-			policy->orig_hostname = g_strdup (hostname);
+			priv->orig_hostname = g_strdup (hostname);
 	}
 
-	policy->fw_manager = nm_firewall_manager_get();
-	id = g_signal_connect (policy->fw_manager, "started",
+	priv->fw_manager = nm_firewall_manager_get();
+	id = g_signal_connect (priv->fw_manager, "started",
 	                       G_CALLBACK (firewall_started), policy);
-	policy->fw_started_id = id;
+	priv->fw_started_id = id;
 
-	policy->dns_manager = nm_dns_manager_get (NULL);
-	policy->config_changed_id = g_signal_connect (policy->dns_manager, "config-changed",
+	priv->dns_manager = nm_dns_manager_get (NULL);
+	priv->config_changed_id = g_signal_connect (priv->dns_manager, "config-changed",
 	                                              G_CALLBACK (dns_config_changed), policy);
 
 	_connect_manager_signal (policy, "state-changed", global_state_changed);
@@ -1979,68 +2106,178 @@ nm_policy_new (NMManager *manager, NMSettings *settings)
 	_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, secret_agent_registered);
 
 	/* Initialize connections' auto-retries */
-	reset_retries_all (policy->settings, NULL);
+	reset_retries_all (priv->settings, NULL);
 
 	initialized = TRUE;
 	return policy;
 }
 
-void
-nm_policy_destroy (NMPolicy *policy)
+NMDevice *
+nm_policy_get_default_ip4_device (NMPolicy *policy)
 {
-	const GSList *connections, *iter;
+	return NM_POLICY_GET_PRIVATE (policy)->default_device4;
+}
 
-	g_return_if_fail (policy != NULL);
+NMDevice *
+nm_policy_get_default_ip6_device (NMPolicy *policy)
+{
+	return NM_POLICY_GET_PRIVATE (policy)->default_device6;
+}
+
+NMDevice *
+nm_policy_get_activating_ip4_device (NMPolicy *policy)
+{
+	return NM_POLICY_GET_PRIVATE (policy)->activating_device4;
+}
+
+NMDevice *
+nm_policy_get_activating_ip6_device (NMPolicy *policy)
+{
+	return NM_POLICY_GET_PRIVATE (policy)->activating_device6;
+}
+
+static void
+nm_policy_init (NMPolicy *policy)
+{
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMPolicy *policy = NM_POLICY (object);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+
+	switch (prop_id) {
+	case PROP_DEFAULT_IP4_DEVICE:
+		g_value_set_object (value, priv->default_device4);
+		break;
+	case PROP_DEFAULT_IP6_DEVICE:
+		g_value_set_object (value, priv->default_device6);
+		break;
+	case PROP_ACTIVATING_IP4_DEVICE:
+		g_value_set_object (value, priv->activating_device4);
+		break;
+	case PROP_ACTIVATING_IP6_DEVICE:
+		g_value_set_object (value, priv->activating_device6);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NMPolicy *policy = NM_POLICY (object);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
+	const GSList *connections, *iter;
 
 	/* Tell any existing hostname lookup thread to die, it'll get cleaned up
 	 * by the lookup thread callback.
 	  */
-	if (policy->lookup) {
-		hostname_thread_kill (policy->lookup);
-		policy->lookup = NULL;
+	if (priv->lookup) {
+		hostname_thread_kill (priv->lookup);
+		priv->lookup = NULL;
 	}
-	g_free (policy->lookup_ipv6_addr);
+	g_free (priv->lookup_ipv6_addr);
 
-	g_slist_foreach (policy->pending_activation_checks, (GFunc) activate_data_free, NULL);
-	g_slist_free (policy->pending_activation_checks);
+	g_slist_free_full (priv->pending_activation_checks, (GDestroyNotify) activate_data_free);
+	priv->pending_activation_checks = NULL;
 
-	g_slist_foreach (policy->pending_secondaries, (GFunc) pending_secondary_data_free, NULL);
-	g_slist_free (policy->pending_secondaries);
+	g_slist_free_full (priv->pending_secondaries, (GDestroyNotify) pending_secondary_data_free);
+	priv->pending_secondaries = NULL;
 
-	g_signal_handler_disconnect (policy->fw_manager, policy->fw_started_id);
-	g_object_unref (policy->fw_manager);
+	if (priv->fw_manager) {
+		g_signal_handler_disconnect (priv->fw_manager, priv->fw_started_id);
+		g_object_unref (priv->fw_manager);
+		priv->fw_manager = NULL;
+	}
 
-	g_signal_handler_disconnect (policy->dns_manager, policy->config_changed_id);
-	g_object_unref (policy->dns_manager);
+	if (priv->dns_manager) {
+		g_signal_handler_disconnect (priv->dns_manager, priv->config_changed_id);
+		g_object_unref (priv->dns_manager);
+		priv->dns_manager = NULL;
+	}
 
-	for (iter = policy->manager_ids; iter; iter = g_slist_next (iter))
-		g_signal_handler_disconnect (policy->manager, GPOINTER_TO_UINT (iter->data));
-	g_slist_free (policy->manager_ids);
+	for (iter = priv->manager_ids; iter; iter = g_slist_next (iter))
+		g_signal_handler_disconnect (priv->manager, GPOINTER_TO_UINT (iter->data));
+	g_slist_free (priv->manager_ids);
+	priv->manager_ids = NULL;
 
-	for (iter = policy->settings_ids; iter; iter = g_slist_next (iter))
-		g_signal_handler_disconnect (policy->settings, GPOINTER_TO_UINT (iter->data));
-	g_slist_free (policy->settings_ids);
+	for (iter = priv->settings_ids; iter; iter = g_slist_next (iter))
+		g_signal_handler_disconnect (priv->settings, GPOINTER_TO_UINT (iter->data));
+	g_slist_free (priv->settings_ids);
+	priv->settings_ids = NULL;
 
-	for (iter = policy->dev_ids; iter; iter = g_slist_next (iter)) {
+	for (iter = priv->dev_ids; iter; iter = g_slist_next (iter)) {
 		DeviceSignalId *data = iter->data;
 
 		g_signal_handler_disconnect (data->device, data->id);
 		g_slice_free (DeviceSignalId, data);
 	}
-	g_slist_free (policy->dev_ids);
+	g_slist_free (priv->dev_ids);
+	priv->dev_ids = NULL;
 
-	connections = nm_manager_get_active_connections (policy->manager);
-	for (iter = connections; iter; iter = g_slist_next (iter))
-		active_connection_removed (policy->manager, NM_ACTIVE_CONNECTION (iter->data), policy);
+	/* The manager should have disposed of ActiveConnections already, which
+	 * will have called active_connection_removed() and thus we don't need
+	 * to clean anything up.  Assert that this is TRUE.
+	 */
+	connections = nm_manager_get_active_connections (priv->manager);
+	g_assert (connections == NULL);
+
+	if (priv->reset_retries_id) {
+		g_source_remove (priv->reset_retries_id);
+		priv->reset_retries_id = 0;
+	}
 
-	if (policy->reset_retries_id)
-		g_source_remove (policy->reset_retries_id);
+	g_free (priv->orig_hostname);
+	priv->orig_hostname = NULL;
+	g_free (priv->cur_hostname);
+	priv->cur_hostname = NULL;
 
-	g_free (policy->orig_hostname);
-	g_free (policy->cur_hostname);
+	g_clear_object (&priv->settings);
 
-	g_object_unref (policy->settings);
-	g_object_unref (policy->manager);
-	g_free (policy);
+	G_OBJECT_CLASS (nm_policy_parent_class)->dispose (object);
 }
 
+static void
+nm_policy_class_init (NMPolicyClass *policy_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (policy_class);
+
+	g_type_class_add_private (policy_class, sizeof (NMPolicyPrivate));
+
+	object_class->get_property = get_property;
+	object_class->dispose = dispose;
+
+	g_object_class_install_property
+		(object_class, PROP_DEFAULT_IP4_DEVICE,
+		 g_param_spec_object (NM_POLICY_DEFAULT_IP4_DEVICE,
+		                      "Default IP4 device",
+		                      "Default IP4 device",
+		                      NM_TYPE_DEVICE,
+		                      G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_DEFAULT_IP6_DEVICE,
+		 g_param_spec_object (NM_POLICY_DEFAULT_IP6_DEVICE,
+		                      "Default IP6 device",
+		                      "Default IP6 device",
+		                      NM_TYPE_DEVICE,
+		                      G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_ACTIVATING_IP4_DEVICE,
+		 g_param_spec_object (NM_POLICY_ACTIVATING_IP4_DEVICE,
+		                      "Activating default IP4 device",
+		                      "Activating default IP4 device",
+		                      NM_TYPE_DEVICE,
+		                      G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_ACTIVATING_IP6_DEVICE,
+		 g_param_spec_object (NM_POLICY_ACTIVATING_IP6_DEVICE,
+		                      "Activating default IP6 device",
+		                      "Activating default IP6 device",
+		                      NM_TYPE_DEVICE,
+		                      G_PARAM_READABLE));
+}