about summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
authorSjoerd Simons <sjoerd@luon.net>2013-10-25 21:20:58 +0200
committerSjoerd Simons <sjoerd@luon.net>2013-10-25 21:20:58 +0200
commita4256940da049f91bbbea5e53e469a59ea9c10f7 (patch)
tree2211c102aea3a33f34f960f48c8b8a55daccd97b /src/nm-manager.c
parent8d275584fc94f398f0a7c990dcd057533d9a5856 (diff)
Imported Upstream version 0.9.8.8 upstream/0.9.8.8
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c424
1 files changed, 359 insertions, 65 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 3dc60d85..9c82d145 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -71,10 +71,8 @@
 #include "wifi-utils.h"
 #include "nm-enum-types.h"
 #include "nm-sleep-monitor.h"
-
-#if WITH_CONCHECK
 #include "nm-connectivity.h"
-#endif
+#include "nm-policy.h"
 
 
 #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
@@ -129,9 +127,13 @@ static void impl_manager_get_logging (NMManager *manager,
                                       char **level,
                                       char **domains);
 
+static void impl_manager_check_connectivity (NMManager *manager,
+                                             DBusGMethodInvocation *context);
+
 #include "nm-manager-glue.h"
 
 static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
+                                           NMBluezDevice *bt_device,
                                            const char *bdaddr,
                                            const char *name,
                                            const char *object_path,
@@ -207,14 +209,17 @@ typedef struct {
 
 	GSList *active_connections;
 	guint ac_cleanup_id;
+	NMActiveConnection *primary_connection;
+	NMActiveConnection *activating_connection;
 
 	GSList *devices;
 	NMState state;
-#if WITH_CONCHECK
 	NMConnectivity *connectivity;
-#endif
+
+	NMPolicy *policy;
 
 	NMDBusManager *dbus_mgr;
+	guint          dbus_connection_changed_id;
 	NMUdevManager *udev_mgr;
 	NMBluezManager *bluez_mgr;
 
@@ -284,6 +289,9 @@ enum {
 	PROP_WIMAX_ENABLED,
 	PROP_WIMAX_HARDWARE_ENABLED,
 	PROP_ACTIVE_CONNECTIONS,
+	PROP_CONNECTIVITY,
+	PROP_PRIMARY_CONNECTION,
+	PROP_ACTIVATING_CONNECTION,
 
 	/* Not exported */
 	PROP_HOSTNAME,
@@ -312,6 +320,14 @@ static void active_connection_state_changed (NMActiveConnection *active,
                                              GParamSpec *pspec,
                                              NMManager *self);
 
+static void
+active_connection_removed (NMManager *self, NMActiveConnection *active)
+{
+	g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
+	g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
+	g_object_unref (active);
+}
+
 static gboolean
 _active_connection_cleanup (gpointer user_data)
 {
@@ -329,9 +345,7 @@ _active_connection_cleanup (gpointer user_data)
 		iter = iter->next;
 		if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
 			priv->active_connections = g_slist_remove (priv->active_connections, ac);
-			g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, ac);
-			g_signal_handlers_disconnect_by_func (ac, active_connection_state_changed, self);
-			g_object_unref (ac);
+			active_connection_removed (self, ac);
 			changed = TRUE;
 		}
 	}
@@ -510,11 +524,77 @@ modem_added (NMModemManager *modem_manager,
 }
 
 static void
+set_state (NMManager *manager, NMState state)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	const char *state_str;
+
+	if (priv->state == state)
+		return;
+
+	priv->state = state;
+
+	switch (state) {
+	case NM_STATE_ASLEEP:
+		state_str = "ASLEEP";
+		break;
+	case NM_STATE_DISCONNECTED:
+		state_str = "DISCONNECTED";
+		break;
+	case NM_STATE_DISCONNECTING:
+		state_str = "DISCONNECTING";
+		break;
+	case NM_STATE_CONNECTING:
+		state_str = "CONNECTING";
+		break;
+	case NM_STATE_CONNECTED_LOCAL:
+		state_str = "CONNECTED_LOCAL";
+		break;
+	case NM_STATE_CONNECTED_SITE:
+		state_str = "CONNECTED_SITE";
+		break;
+	case NM_STATE_CONNECTED_GLOBAL:
+		state_str = "CONNECTED_GLOBAL";
+		break;
+	case NM_STATE_UNKNOWN:
+	default:
+		state_str = "UNKNOWN";
+		break;
+	}
+
+	nm_log_info (LOGD_CORE, "NetworkManager state is now %s", state_str);
+
+	g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE);
+	g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state);
+}
+
+static void
+checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	NMManager *manager = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMConnectivityState connectivity;
+
+	if (priv->state == NM_STATE_CONNECTING || priv->state == NM_STATE_CONNECTED_SITE) {
+		connectivity = nm_connectivity_check_finish (priv->connectivity, result, NULL);
+
+		if (connectivity == NM_CONNECTIVITY_FULL)
+			set_state (manager, NM_STATE_CONNECTED_GLOBAL);
+		else if (   connectivity == NM_CONNECTIVITY_PORTAL
+		         || connectivity == NM_CONNECTIVITY_LIMITED)
+			set_state (manager, NM_STATE_CONNECTED_SITE);
+	}
+
+	g_object_unref (manager);
+}
+
+static void
 nm_manager_update_state (NMManager *manager)
 {
 	NMManagerPrivate *priv;
 	NMState new_state = NM_STATE_DISCONNECTED;
 	GSList *iter;
+	gboolean want_connectivity_check = FALSE;
 
 	g_return_if_fail (NM_IS_MANAGER (manager));
 
@@ -528,13 +608,14 @@ nm_manager_update_state (NMManager *manager)
 			NMDeviceState state = nm_device_get_state (dev);
 
 			if (state == NM_DEVICE_STATE_ACTIVATED) {
-				new_state = NM_STATE_CONNECTED_GLOBAL;
-#if WITH_CONCHECK
-				/* Connectivity check might have a better idea */
-				if (nm_connectivity_get_connected (priv->connectivity) == FALSE)
-					new_state = NM_STATE_CONNECTED_SITE;
-#endif
-				break;
+				nm_connectivity_set_online (priv->connectivity, TRUE);
+				if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) {
+					new_state = NM_STATE_CONNECTING;
+					want_connectivity_check = TRUE;
+				} else {
+					new_state = NM_STATE_CONNECTED_GLOBAL;
+					break;
+				}
 			}
 
 			if (nm_device_is_activating (dev))
@@ -546,12 +627,15 @@ nm_manager_update_state (NMManager *manager)
 		}
 	}
 
-	if (priv->state != new_state) {
-		priv->state = new_state;
-		g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE);
-
-		g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state);
+	if (new_state == NM_STATE_CONNECTING && want_connectivity_check) {
+		nm_connectivity_check_async (priv->connectivity,
+		                             checked_connectivity,
+		                             g_object_ref (manager));
+		return;
 	}
+
+	nm_connectivity_set_online (priv->connectivity, new_state >= NM_STATE_CONNECTED_LOCAL);
+	set_state (manager, new_state);
 }
 
 static void
@@ -562,9 +646,6 @@ manager_device_state_changed (NMDevice *device,
                               gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
-#if WITH_CONCHECK
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-#endif
 
 	switch (new_state) {
 	case NM_DEVICE_STATE_UNMANAGED:
@@ -579,23 +660,6 @@ manager_device_state_changed (NMDevice *device,
 	}
 
 	nm_manager_update_state (self);
-
-#if WITH_CONCHECK
-	if (priv->state >= NM_STATE_CONNECTED_LOCAL) {
-		if (old_state == NM_DEVICE_STATE_ACTIVATED || new_state == NM_DEVICE_STATE_ACTIVATED) {
-			/* Still connected, but a device activated or deactivated; make sure
-			 * we still have connectivity on the other activated devices.
-			 */
-			nm_log_dbg (LOGD_CORE, "(%s): triggered connectivity check due to state change",
-			            nm_device_get_iface (device));
-			nm_connectivity_start_check (priv->connectivity);
-		}
-	} else {
-		/* Cannot be connected if no devices are activated */
-		nm_log_dbg (LOGD_CORE, "stopping connectivity checks");
-		nm_connectivity_stop_check (priv->connectivity);
-	}
-#endif
 }
 
 /* Removes a device from a device list; returns the start of the new device list */
@@ -1029,7 +1093,7 @@ find_vlan_parent (NMManager *self,
 					if (req) {
 						candidate = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (req));
 						if (candidate == parent_connection)
-							return parent;
+							return NM_DEVICE (iter->data);
 					}
 				}
 
@@ -1228,7 +1292,7 @@ bridge_created_by_nm (NMManager *self, const char *iface)
  * If @connection requires a virtual device and one does not yet exist for it,
  * creates that device.
  *
- * Returns: the #NMDevice if successfully created, NULL if not
+ * Returns: the #NMDevice if successfully created, %NULL if not
  */
 static NMDevice *
 system_create_virtual_device (NMManager *self, NMConnection *connection)
@@ -1957,6 +2021,7 @@ add_device (NMManager *self, NMDevice *device)
 
 static void
 bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
+                               NMBluezDevice *bt_device,
                                const char *bdaddr,
                                const char *name,
                                const char *object_path,
@@ -1971,12 +2036,13 @@ bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
 	g_return_if_fail (name != NULL);
 	g_return_if_fail (object_path != NULL);
 	g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE);
+	g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device));
 
 	/* Make sure the device is not already in the device list */
 	if (nm_manager_get_device_by_udi (manager, object_path))
 		return;
 
-	device = nm_device_bt_new (object_path, bdaddr, name, capabilities, FALSE);
+	device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities, FALSE);
 	if (device) {
 		nm_log_info (LOGD_HW, "BT device %s (%s) added (%s%s%s)",
 		             name,
@@ -2791,6 +2857,7 @@ nm_manager_activate_connection (NMManager *manager,
 	NMDevice *master_device = NULL;
 	NMConnection *master_connection = NULL;
 	NMActiveConnection *master_ac = NULL, *ac = NULL;
+	gboolean matched;
 
 	g_return_val_if_fail (manager != NULL, NULL);
 	g_return_val_if_fail (connection != NULL, NULL);
@@ -2838,13 +2905,15 @@ nm_manager_activate_connection (NMManager *manager,
 				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
 					                 "Failed to determine connection's virtual interface name");
 				return NULL;
-			} else if (g_strcmp0 (iface, nm_device_get_ip_iface (device)) != 0) {
+			}
+
+			matched = g_str_equal (iface, nm_device_get_ip_iface (device));
+			g_free (iface);
+			if (!matched) {
 				g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
 					                 "Device given by path did not match connection's virtual interface name");
-				g_free (iface);
 				return NULL;
 			}
-			g_free (iface);
 		}
 	} else {
 		/* Virtual connections (VLAN, bond, etc) may not specify a device
@@ -2866,6 +2935,7 @@ nm_manager_activate_connection (NMManager *manager,
 		}
 
 		device = find_device_by_ip_iface (manager, iface);
+		g_free (iface);
 		if (!device) {
 			/* Create it */
 			device = system_create_virtual_device (manager, connection);
@@ -3149,6 +3219,9 @@ nm_manager_deactivate_connection (NMManager *manager,
 			vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED;
 		if (nm_vpn_manager_deactivate_connection (priv->vpn_manager, NM_VPN_CONNECTION (active), vpn_reason))
 			success = TRUE;
+		else
+			g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+			                     "The VPN connection was not active.");
 	} else {
 		g_assert (NM_IS_ACT_REQUEST (active));
 		/* FIXME: use DEACTIVATING state */
@@ -3717,6 +3790,103 @@ impl_manager_get_logging (NMManager *manager,
 	*domains = g_strdup (nm_logging_domains_to_string ());
 }
 
+static void
+connectivity_check_done (GObject *object,
+                         GAsyncResult *result,
+                         gpointer user_data)
+{
+	DBusGMethodInvocation *context = user_data;
+	NMConnectivityState state;
+	GError *error = NULL;
+
+	state = nm_connectivity_check_finish (NM_CONNECTIVITY (object), result, &error);
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	} else
+		dbus_g_method_return (context, state);
+}
+
+
+static void
+check_connectivity_auth_done_cb (NMAuthChain *chain,
+                                 GError *auth_error,
+                                 DBusGMethodInvocation *context,
+                                 gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	GError *error = NULL;
+	NMAuthCallResult result;
+
+	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL);
+
+	if (auth_error) {
+		nm_log_dbg (LOGD_CORE, "CheckConnectivity request failed: %s", auth_error->message);
+		error = g_error_new (NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     "Connectivity check request failed: %s",
+		                     auth_error->message);
+	} else if (result != NM_AUTH_CALL_RESULT_YES) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Not authorized to recheck connectivity");
+	} else {
+		/* it's allowed */
+		nm_connectivity_check_async (priv->connectivity,
+		                             connectivity_check_done,
+		                             context);
+	}
+
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+	}
+	nm_auth_chain_unref (chain);
+}
+
+static void
+impl_manager_check_connectivity (NMManager *manager,
+                                 DBusGMethodInvocation *context)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMAuthChain *chain;
+	gulong sender_uid = G_MAXULONG;
+	char *error_desc = NULL;
+	GError *error;
+
+	/* Need to check the caller's permissions and stuff first */
+	if (!nm_auth_get_caller_uid (context, 
+	                             priv->dbus_mgr,
+	                             &sender_uid,
+	                             &error_desc)) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             error_desc);
+		dbus_g_method_return_error (context, error);
+		g_error_free (error);
+		g_free (error_desc);
+		return;
+	}
+
+	/* Yay for root */
+	if (0 == sender_uid) {
+		nm_connectivity_check_async (priv->connectivity,
+		                             connectivity_check_done,
+		                             context);
+		return;
+	}
+
+	/* Validate the user request */
+	chain = nm_auth_chain_new (context, NULL, check_connectivity_auth_done_cb, manager);
+	g_assert (chain);
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+}
+
 void
 nm_manager_start (NMManager *self)
 {
@@ -3813,22 +3983,21 @@ handle_firmware_changed (gpointer user_data)
 	return FALSE;
 }
 
-#if WITH_CONCHECK
 static void
 connectivity_changed (NMConnectivity *connectivity,
                       GParamSpec *pspec,
                       gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
-	gboolean connected;
+	NMConnectivityState state;
+	static const char *connectivity_states[] = { "UNKNOWN", "NONE", "PORTAL", "LIMITED", "FULL" };
 
-	connected = nm_connectivity_get_connected (connectivity);
+	state = nm_connectivity_get_state (connectivity);
 	nm_log_dbg (LOGD_CORE, "connectivity checking indicates %s",
-	            connected ? "CONNECTED" : "NOT CONNECTED");
+	            connectivity_states[state]);
 
 	nm_manager_update_state (self);
 }
-#endif  /* WITH_CONCHECK */
 
 static void
 firmware_dir_changed (GFileMonitor *monitor,
@@ -3859,6 +4028,68 @@ firmware_dir_changed (GFileMonitor *monitor,
 	}
 }
 
+static void
+policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *best;
+	NMActiveConnection *ac;
+
+	/* Note: this assumes that it's not possible for the IP4 default
+	 * route to be going over the default-ip6-device. If that changes,
+	 * we need something more complicated here.
+	 */
+	best = nm_policy_get_default_ip4_device (priv->policy);
+	if (!best)
+		best = nm_policy_get_default_ip6_device (priv->policy);
+
+	if (best)
+		ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (best));
+	else
+		ac = NULL;
+
+	if (ac != priv->primary_connection) {
+		g_clear_object (&priv->primary_connection);
+		priv->primary_connection = ac ? g_object_ref (ac) : NULL;
+		nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)");
+		g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION);
+	}
+}
+
+static void
+policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMDevice *activating, *best;
+	NMActiveConnection *ac;
+
+	/* We only look at activating-ip6-device if activating-ip4-device
+	 * AND default-ip4-device are NULL; if default-ip4-device is
+	 * non-NULL, then activating-ip6-device is irrelevant, since while
+	 * that device might become the new default-ip6-device, it can't
+	 * become primary-connection while default-ip4-device is set to
+	 * something else.
+	 */
+	activating = nm_policy_get_activating_ip4_device (priv->policy);
+	best = nm_policy_get_default_ip4_device (priv->policy);
+	if (!activating && !best)
+		activating = nm_policy_get_activating_ip6_device (priv->policy);
+
+	if (activating)
+		ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (activating));
+	else
+		ac = NULL;
+
+	if (ac != priv->activating_connection) {
+		g_clear_object (&priv->activating_connection);
+		priv->activating_connection = ac ? g_object_ref (ac) : NULL;
+		nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)");
+		g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVATING_CONNECTION);
+	}
+}
+
 #define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
 #define DEV_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.Device.PermissionDenied"
 
@@ -4089,12 +4320,19 @@ nm_manager_new (NMSettings *settings,
 
 	priv = NM_MANAGER_GET_PRIVATE (singleton);
 
-#if WITH_CONCHECK
-	priv->connectivity = nm_connectivity_new (connectivity_uri, connectivity_interval, connectivity_response);
+	priv->policy = nm_policy_new (singleton, settings);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE,
+	                  G_CALLBACK (policy_default_device_changed), singleton);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE,
+	                  G_CALLBACK (policy_default_device_changed), singleton);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE,
+	                  G_CALLBACK (policy_activating_device_changed), singleton);
+	g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE,
+	                  G_CALLBACK (policy_activating_device_changed), singleton);
 
-	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_CONNECTED,
+	priv->connectivity = nm_connectivity_new (connectivity_uri, connectivity_interval, connectivity_response);
+	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
 	                  G_CALLBACK (connectivity_changed), singleton);
-#endif
 
 	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
 	g_assert (bus);
@@ -4210,21 +4448,21 @@ dispose (GObject *object)
 		priv->ac_cleanup_id = 0;
 	}
 
-	for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
-		g_signal_handlers_disconnect_by_func (iter->data, active_connection_state_changed, object);
-		g_object_unref (iter->data);
-	}
+	for (iter = priv->active_connections; iter; iter = g_slist_next (iter))
+		active_connection_removed (manager, NM_ACTIVE_CONNECTION (iter->data));
 	g_slist_free (priv->active_connections);
+	priv->active_connections = NULL;
+	g_clear_object (&priv->primary_connection);
+	g_clear_object (&priv->activating_connection);
 
-#if WITH_CONCHECK
-	if (priv->connectivity) {
-		g_object_unref (priv->connectivity);
-		priv->connectivity = NULL;
-	}
-#endif
+	g_clear_object (&priv->connectivity);
 
 	g_free (priv->hostname);
 
+	g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_default_device_changed), singleton);
+	g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_activating_device_changed), singleton);
+	g_object_unref (priv->policy);
+
 	g_object_unref (priv->settings);
 	g_object_unref (priv->vpn_manager);
 
@@ -4245,6 +4483,7 @@ dispose (GObject *object)
 		g_assert (dbus_connection);
 		dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
 	}
+	g_signal_handler_disconnect (priv->dbus_mgr, priv->dbus_connection_changed_id);
 	g_object_unref (priv->dbus_mgr);
 
 	if (priv->bluez_mgr)
@@ -4459,6 +4698,17 @@ get_property (GObject *object, guint prop_id,
 		}
 		g_value_take_boxed (value, active);
 		break;
+	case PROP_CONNECTIVITY:
+		g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity));
+		break;
+	case PROP_PRIMARY_CONNECTION:
+		path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : "/";
+		g_value_set_boxed (value, path);
+		break;
+	case PROP_ACTIVATING_CONNECTION:
+		path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : "/";
+		g_value_set_boxed (value, path);
+		break;
 	case PROP_HOSTNAME:
 		g_value_set_string (value, priv->hostname);
 		break;
@@ -4492,6 +4742,22 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 }
 
 static void
+dbus_connection_changed_cb (NMDBusManager *dbus_mgr,
+                            DBusConnection *dbus_connection,
+                            gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+
+	if (dbus_connection) {
+		/* Register property filter on new connection; there's no reason this
+		 * should fail except out-of-memory or program error; if it does fail
+		 * then there's no Manager property access control, which is bad.
+		 */
+		g_assert (dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL));
+	}
+}
+
+static void
 nm_manager_init (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
@@ -4533,6 +4799,10 @@ nm_manager_init (NMManager *manager)
 	priv->state = NM_STATE_DISCONNECTED;
 
 	priv->dbus_mgr = nm_dbus_manager_get ();
+	priv->dbus_connection_changed_id = g_signal_connect (priv->dbus_mgr,
+	                                                     NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED,
+	                                                     G_CALLBACK (dbus_connection_changed_cb),
+	                                                     manager);
 
 	priv->modem_manager = nm_modem_manager_get ();
 	priv->modem_added_id = g_signal_connect (priv->modem_manager, "modem-added",
@@ -4694,6 +4964,30 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
 		                     G_PARAM_READABLE));
 
+	g_object_class_install_property
+		(object_class, PROP_CONNECTIVITY,
+		 g_param_spec_uint (NM_MANAGER_CONNECTIVITY,
+		                    "Connectivity",
+		                    "Connectivity state",
+		                    NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_PRIMARY_CONNECTION,
+		 g_param_spec_boxed (NM_MANAGER_PRIMARY_CONNECTION,
+		                     "Primary connection",
+		                     "Primary connection",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_ACTIVATING_CONNECTION,
+		 g_param_spec_boxed (NM_MANAGER_ACTIVATING_CONNECTION,
+		                     "Activating connection",
+		                     "Activating connection",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
 	/* Hostname is not exported over D-Bus */
 	g_object_class_install_property
 		(object_class, PROP_HOSTNAME,