summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2013-07-30 16:31:31 -0400
committerDan Winship <danw@gnome.org>2013-08-28 10:54:08 -0400
commit07521da59115dff6c2394240d5fffade3cded62f (patch)
treea45a32fc4c7113f027501d775c78e08e58dfe9a0
parent8732914815ef315cd94ba67ba4b8247d90412b13 (diff)
downloadNetworkManager-07521da59115dff6c2394240d5fffade3cded62f.tar.gz
core: provide additional network connectivity information
NM_STATE_CONNECTED_SITE doesn't distinguish between "behind a captive portal" and "limited network connectivity" (ie, connected to a router that has lost its upstream connection). Add a new NMManager :connectivity property to provide this information. Also add a CheckConnectivity method, which can be used to force NM to re-check the connectivity state, which could be called by a client after it completed a portal login, or fixed a network problem.
-rw-r--r--include/NetworkManager.h21
-rw-r--r--introspection/nm-manager.xml53
-rw-r--r--src/nm-connectivity.c76
-rw-r--r--src/nm-connectivity.h22
-rw-r--r--src/nm-manager.c115
-rw-r--r--src/nm-manager.h1
6 files changed, 237 insertions, 51 deletions
diff --git a/include/NetworkManager.h b/include/NetworkManager.h
index 27c20f6000..4aa6e9e72d 100644
--- a/include/NetworkManager.h
+++ b/include/NetworkManager.h
@@ -102,6 +102,27 @@ typedef enum {
#define NM_STATE_CONNECTED NM_STATE_CONNECTED_GLOBAL
/**
+ * NMConnectivityState:
+ * @NM_CONNECTIVITY_UNKNOWN: Network connectivity is unknown.
+ * @NM_CONNECTIVITY_NONE: The host is not connected to any network.
+ * @NM_CONNECTIVITY_PORTAL: The host is behind a captive portal and
+ * cannot reach the full Internet.
+ * @NM_CONNECTIVITY_LIMITED: The host is connected to a network, but
+ * does not appear to be able to reach the full Internet.
+ * @NM_CONNECTIVITY_FULL: The host is connected to a network, and
+ * appears to be able to reach the full Internet.
+ *
+ * Since: 0.9.10
+ */
+typedef enum {
+ NM_CONNECTIVITY_UNKNOWN,
+ NM_CONNECTIVITY_NONE,
+ NM_CONNECTIVITY_PORTAL,
+ NM_CONNECTIVITY_LIMITED,
+ NM_CONNECTIVITY_FULL
+} NMConnectivityState;
+
+/**
* NMDeviceType:
* @NM_DEVICE_TYPE_UNKNOWN: unknown device
* @NM_DEVICE_TYPE_GENERIC: generic support for unrecognized device types
diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml
index d9f6a7c3d2..4fc7692cc4 100644
--- a/introspection/nm-manager.xml
+++ b/introspection/nm-manager.xml
@@ -244,6 +244,19 @@
</arg>
</method>
+ <method name="CheckConnectivity">
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_manager_check_connectivity"/>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <tp:docstring>
+ Re-check the network connectivity state.
+ </tp:docstring>
+ <arg name="connectivity" type="u" tp:type="NM_CONNECTIVITY" direction="out">
+ <tp:docstring>
+ The current connectivity state.
+ </tp:docstring>
+ </arg>
+ </method>
+
<method name="state">
<tp:docstring>
The overall networking state as determined by the NetworkManager daemon,
@@ -333,6 +346,12 @@
</arg>
</signal>
+ <property name="Connectivity" type="u" access="read" tp:type="NM_CONNECTIVITY">
+ <tp:docstring>
+ The network connectivity state.
+ </tp:docstring>
+ </property>
+
<signal name="PropertiesChanged">
<tp:docstring>
NetworkManager's properties changed.
@@ -413,5 +432,39 @@
</tp:enumvalue>
</tp:enum>
+ <tp:enum name="NM_CONNECTIVITY" type="u">
+ <tp:docstring>
+ Describes the network-connectivity state.
+ </tp:docstring>
+ <tp:enumvalue suffix="UNKNOWN" value="0">
+ <tp:docstring>
+ Network connectivity is unknown.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="NONE" value="1">
+ <tp:docstring>
+ The host is not connected to any network.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="PORTAL" value="2">
+ <tp:docstring>
+ The host is behind a captive portal and cannot reach the
+ full Internet.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="LIMITED" value="3">
+ <tp:docstring>
+ The host is connected to a network, but does not appear to
+ be able to reach the full Internet.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="FULL" value="4">
+ <tp:docstring>
+ The host is connected to a network, and appears to be able
+ to reach the full Internet
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
</interface>
</node>
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 2b6c25944b..16d8e26688 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -48,7 +48,7 @@ typedef struct {
guint check_id;
#endif
- gboolean connected;
+ NMConnectivityState state;
} NMConnectivityPrivate;
enum {
@@ -56,28 +56,28 @@ enum {
PROP_URI,
PROP_INTERVAL,
PROP_RESPONSE,
- PROP_CONNECTED,
+ PROP_STATE,
LAST_PROP
};
-gboolean
-nm_connectivity_get_connected (NMConnectivity *connectivity)
+NMConnectivityState
+nm_connectivity_get_state (NMConnectivity *connectivity)
{
- g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), NM_CONNECTIVITY_UNKNOWN);
- return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->connected;
+ return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state;
}
static void
-update_connected (NMConnectivity *self, gboolean connected)
+update_state (NMConnectivity *self, NMConnectivityState state)
{
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
- gboolean old_connected = priv->connected;
- priv->connected = connected;
- if (priv->connected != old_connected)
- g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_CONNECTED);
+ if (priv->state != state) {
+ priv->state = state;
+ g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_STATE);
+ }
}
#if WITH_CONCHECK
@@ -87,37 +87,47 @@ nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_
GSimpleAsyncResult *simple = user_data;
NMConnectivity *self;
NMConnectivityPrivate *priv;
- gboolean connected_new = FALSE;
+ NMConnectivityState new_state;
const char *nm_header;
self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
g_object_unref (self);
priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+ if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
+ nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' failed with '%s'.",
+ priv->uri, msg->reason_phrase);
+ new_state = NM_CONNECTIVITY_LIMITED;
+ goto done;
+ }
+
/* Check headers; if we find the NM-specific one we're done */
nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status");
if (g_strcmp0 (nm_header, "online") == 0) {
nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' with Status header successful.", priv->uri);
- connected_new = TRUE;
+ new_state = NM_CONNECTIVITY_FULL;
} else if (msg->status_code == SOUP_STATUS_OK) {
/* check response */
if (msg->response_body->data && (g_str_has_prefix (msg->response_body->data, priv->response))) {
nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' successful.",
priv->uri);
- connected_new = TRUE;
+ new_state = NM_CONNECTIVITY_FULL;
} else {
- nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' did not match expected response '%s'.",
+ nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' did not match expected response '%s'; assuming captive portal.",
priv->uri, priv->response);
+ new_state = NM_CONNECTIVITY_PORTAL;
}
} else {
- nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' returned status '%d %s'.",
+ nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' returned status '%d %s'; assuming captive portal.",
priv->uri, msg->status_code, msg->reason_phrase);
+ new_state = NM_CONNECTIVITY_PORTAL;
}
- g_simple_async_result_set_op_res_gboolean (simple, connected_new);
+ done:
+ g_simple_async_result_set_op_res_gssize (simple, new_state);
g_simple_async_result_complete (simple);
- update_connected (self, connected_new);
+ update_state (self, new_state);
}
static void
@@ -174,7 +184,7 @@ nm_connectivity_set_online (NMConnectivity *self,
/* Either @online is %TRUE but we aren't checking connectivity, or
* @online is %FALSE. Either way we can update our status immediately.
*/
- update_connected (self, online);
+ update_state (self, online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE);
}
void
@@ -207,23 +217,23 @@ nm_connectivity_check_async (NMConnectivity *self,
}
#endif
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_set_op_res_gssize (simple, priv->state);
g_simple_async_result_complete_in_idle (simple);
}
-gboolean
+NMConnectivityState
nm_connectivity_check_finish (NMConnectivity *self,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), FALSE);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_connectivity_check_async), NM_CONNECTIVITY_UNKNOWN);
simple = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (simple, error))
- return FALSE;
- return g_simple_async_result_get_op_res_gboolean (simple);
+ return NM_CONNECTIVITY_UNKNOWN;
+ return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple);
}
@@ -243,7 +253,7 @@ nm_connectivity_new (void)
NM_CONNECTIVITY_RESPONSE, check_response ? check_response : DEFAULT_RESPONSE,
NULL);
g_return_val_if_fail (self != NULL, NULL);
- update_connected (self, FALSE);
+ update_state (self, NM_CONNECTIVITY_NONE);
return self;
}
@@ -314,8 +324,8 @@ get_property (GObject *object, guint property_id,
case PROP_RESPONSE:
g_value_set_string (value, priv->response);
break;
- case PROP_CONNECTED:
- g_value_set_boolean (value, priv->connected);
+ case PROP_STATE:
+ g_value_set_uint (value, priv->state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -395,11 +405,11 @@ nm_connectivity_class_init (NMConnectivityClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property
- (object_class, PROP_CONNECTED,
- g_param_spec_boolean (NM_CONNECTIVITY_CONNECTED,
- "Connected",
- "Is connected",
- FALSE,
- G_PARAM_READABLE));
+ (object_class, PROP_STATE,
+ g_param_spec_uint (NM_CONNECTIVITY_STATE,
+ "State",
+ "Connectivity state",
+ NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
+ G_PARAM_READABLE));
}
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index 36d6e8d12a..04d0f2a309 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -38,7 +38,7 @@
#define NM_CONNECTIVITY_URI "uri"
#define NM_CONNECTIVITY_INTERVAL "interval"
#define NM_CONNECTIVITY_RESPONSE "response"
-#define NM_CONNECTIVITY_CONNECTED "connected"
+#define NM_CONNECTIVITY_STATE "state"
typedef struct {
GObject parent;
@@ -50,18 +50,18 @@ typedef struct {
GType nm_connectivity_get_type (void);
-NMConnectivity *nm_connectivity_new (void);
+NMConnectivity *nm_connectivity_new (void);
-void nm_connectivity_set_online (NMConnectivity *self,
- gboolean online);
+void nm_connectivity_set_online (NMConnectivity *self,
+ gboolean online);
-gboolean nm_connectivity_get_connected (NMConnectivity *self);
+NMConnectivityState nm_connectivity_get_state (NMConnectivity *self);
-void nm_connectivity_check_async (NMConnectivity *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean nm_connectivity_check_finish (NMConnectivity *self,
- GAsyncResult *result,
- GError **error);
+void nm_connectivity_check_async (NMConnectivity *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NMConnectivityState nm_connectivity_check_finish (NMConnectivity *self,
+ GAsyncResult *result,
+ GError **error);
#endif /* NM_CONNECTIVITY_H */
diff --git a/src/nm-manager.c b/src/nm-manager.c
index df7d8cfb67..b799d3e204 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -132,6 +132,9 @@ 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,
@@ -291,6 +294,7 @@ enum {
PROP_WIMAX_ENABLED,
PROP_WIMAX_HARDWARE_ENABLED,
PROP_ACTIVE_CONNECTIONS,
+ PROP_CONNECTIVITY,
/* Not exported */
PROP_HOSTNAME,
@@ -577,11 +581,15 @@ 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) {
- if (nm_connectivity_check_finish (priv->connectivity, result, NULL))
+ connectivity = nm_connectivity_check_finish (priv->connectivity, result, NULL);
+
+ if (connectivity == NM_CONNECTIVITY_FULL)
set_state (manager, NM_STATE_CONNECTED_GLOBAL);
- else
+ else if ( connectivity == NM_CONNECTIVITY_PORTAL
+ || connectivity == NM_CONNECTIVITY_LIMITED)
set_state (manager, NM_STATE_CONNECTED_SITE);
}
@@ -609,7 +617,7 @@ nm_manager_update_state (NMManager *manager)
if (state == NM_DEVICE_STATE_ACTIVATED) {
nm_connectivity_set_online (priv->connectivity, TRUE);
- if (!nm_connectivity_get_connected (priv->connectivity)) {
+ if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) {
new_state = NM_STATE_CONNECTING;
want_connectivity_check = TRUE;
} else {
@@ -3953,6 +3961,87 @@ 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;
+ const char *error_desc = NULL;
+ GError *error;
+
+ /* Validate the user request */
+ chain = nm_auth_chain_new (context, check_connectivity_auth_done_cb, manager, &error_desc);
+ if (chain) {
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+ } else {
+ 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);
+ }
+}
+
void
nm_manager_start (NMManager *self)
{
@@ -4050,11 +4139,12 @@ connectivity_changed (NMConnectivity *connectivity,
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);
}
@@ -4267,7 +4357,7 @@ nm_manager_new (NMSettings *settings,
priv = NM_MANAGER_GET_PRIVATE (singleton);
priv->connectivity = nm_connectivity_new ();
- g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_CONNECTED,
+ g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
G_CALLBACK (connectivity_changed), singleton);
bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
@@ -4639,6 +4729,9 @@ 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_HOSTNAME:
g_value_set_string (value, priv->hostname);
break;
@@ -4903,6 +4996,14 @@ 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));
+
/* Hostname is not exported over D-Bus */
g_object_class_install_property
(object_class, PROP_HOSTNAME,
diff --git a/src/nm-manager.h b/src/nm-manager.h
index 6e376787e1..79165f0f4c 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -60,6 +60,7 @@ typedef enum {
#define NM_MANAGER_WIMAX_ENABLED "wimax-enabled"
#define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled"
#define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections"
+#define NM_MANAGER_CONNECTIVITY "connectivity"
/* Not exported */
#define NM_MANAGER_HOSTNAME "hostname"