diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index de7943d518..48e509bef8 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -1077,13 +1077,30 @@ check_connection_compatible (NMDevice *device, return FALSE; } - if ( (g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0) - && !(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - "Access Point (AP) mode is not supported by this device."); - return FALSE; + /* Early exit if supplicant or device don't support AP mode */ + if (g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, + "Access Point (AP) mode is not supported by this device."); + return FALSE; + } + + if (priv->supplicant.iface) { + switch (nm_supplicant_interface_get_ap_support (priv->supplicant.iface)) { + case AP_SUPPORT_NO: + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, + "Access Point (AP) mode is not supported by the supplicant."); + return FALSE; + case AP_SUPPORT_YES: + case AP_SUPPORT_UNKNOWN: + default: + break; + } + } } // FIXME: check channel/freq/band against bands the hardware supports @@ -2810,6 +2827,12 @@ build_supplicant_config (NMDeviceWifi *self, } } + /* Warn if AP mode may not be supported */ + if ( g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0 + && nm_supplicant_interface_get_ap_support (priv->supplicant.iface) == AP_SUPPORT_UNKNOWN) { + nm_log_warn (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out."); + } + if (!nm_supplicant_config_add_setting_wireless (config, s_wireless, nm_ap_get_broadcast (ap), diff --git a/src/nm-device-wifi.h b/src/nm-device-wifi.h index b22c78b3a3..26d66ffcf8 100644 --- a/src/nm-device-wifi.h +++ b/src/nm-device-wifi.h @@ -47,6 +47,7 @@ typedef enum { NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, /*< nick=AccessPointNotFound >*/ NM_WIFI_ERROR_SCAN_NOT_ALLOWED, /*< nick=ScanNotAllowed >*/ + NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, /*< nick=ApModeUnsupported >*/ } NMWifiError; #define NM_DEVICE_WIFI_HW_ADDRESS "hw-address" diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c index 45fe77afd6..75450a0a14 100644 --- a/src/supplicant-manager/nm-supplicant-interface.c +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -86,6 +86,7 @@ typedef struct { char * dev; gboolean is_wireless; gboolean has_credreq; /* Whether querying 802.1x credentials is supported */ + ApSupport ap_support; /* Lightweight AP mode support */ gboolean fast_supported; guint32 max_scan_ssids; guint32 ready_count; @@ -757,6 +758,7 @@ wpas_iface_check_network_reply (NMSupplicantInterface *self) NMSupplicantInfo *info; DBusGProxyCall *call; + priv->ready_count++; info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls); call = dbus_g_proxy_begin_call (priv->iface_proxy, "NetworkReply", iface_check_netreply_cb, @@ -769,6 +771,76 @@ wpas_iface_check_network_reply (NMSupplicantInterface *self) nm_supplicant_info_set_call (info, call); } +ApSupport +nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->ap_support; +} + +void +nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, + ApSupport ap_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Use the best indicator of support between the supplicant global + * Capabilities property and the interface's introspection data. + */ + if (ap_support > priv->ap_support) + priv->ap_support = ap_support; +} + +static void +iface_check_ap_mode_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMSupplicantInfo *info = (NMSupplicantInfo *) user_data; + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface); + char *data; + + /* The ProbeRequest method only exists if AP mode has been enabled */ + if (dbus_g_proxy_end_call (proxy, call_id, NULL, + G_TYPE_STRING, + &data, + G_TYPE_INVALID)) { + if (data && strstr (data, "ProbeRequest")) + priv->ap_support = AP_SUPPORT_YES; + g_free (data); + } + + iface_check_ready (info->interface); +} + +static void +wpas_iface_check_ap_mode (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInfo *info; + DBusGProxyCall *call; + DBusGProxy *proxy; + + priv->ready_count++; + + proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + WPAS_DBUS_SERVICE, + priv->object_path, + DBUS_INTERFACE_INTROSPECTABLE); + + info = nm_supplicant_info_new (self, proxy, priv->other_pcalls); + g_object_unref (proxy); + + /* If the global supplicant capabilities property is not present, we can + * fall back to checking whether the ProbeRequest method is supported. If + * neither of these works we have no way of determining if AP mode is + * supported or not. hostap 1.0 and earlier don't support either of these. + */ + call = dbus_g_proxy_begin_call (proxy, "Introspect", + iface_check_ap_mode_cb, + info, + nm_supplicant_info_destroy, + G_TYPE_INVALID); + nm_supplicant_info_set_call (info, call); +} + static void interface_add_done (NMSupplicantInterface *self, char *path) { @@ -841,9 +913,13 @@ interface_add_done (NMSupplicantInterface *self, char *path) path, DBUS_INTERFACE_PROPERTIES); /* Get initial properties and check whether NetworkReply is supported */ + priv->ready_count = 1; wpas_iface_get_props (self); + + /* These two increment ready_count themselves */ wpas_iface_check_network_reply (self); - priv->ready_count = 2; + if (priv->ap_support == AP_SUPPORT_UNKNOWN) + wpas_iface_check_ap_mode (self); } static void @@ -1406,6 +1482,7 @@ nm_supplicant_interface_new (NMSupplicantManager *smgr, const char *ifname, gboolean is_wireless, gboolean fast_supported, + ApSupport ap_support, gboolean start_now) { NMSupplicantInterface *self; @@ -1429,6 +1506,7 @@ nm_supplicant_interface_new (NMSupplicantManager *smgr, priv->dev = g_strdup (ifname); priv->is_wireless = is_wireless; priv->fast_supported = fast_supported; + priv->ap_support = ap_support; if (start_now) interface_add (self, priv->is_wireless); diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h index 73cf5b524e..2f0233cf78 100644 --- a/src/supplicant-manager/nm-supplicant-interface.h +++ b/src/supplicant-manager/nm-supplicant-interface.h @@ -64,6 +64,12 @@ enum { #define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" +typedef enum { + AP_SUPPORT_UNKNOWN = 0, /* Can't detect whether supported or not */ + AP_SUPPORT_NO = 1, /* AP mode definitely not supported */ + AP_SUPPORT_YES = 2, /* AP mode definitely supported */ +} ApSupport; + struct _NMSupplicantInterface { GObject parent; }; @@ -118,6 +124,7 @@ NMSupplicantInterface * nm_supplicant_interface_new (NMSupplicantManager * smgr, const char *ifname, gboolean is_wireless, gboolean fast_supported, + ApSupport ap_support, gboolean start_now); gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface, @@ -150,4 +157,9 @@ gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, const char *value, GError **error); +ApSupport nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self); + +void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, + ApSupport apmode); + #endif /* NM_SUPPLICANT_INTERFACE_H */ diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c index ae15086516..430a3852c3 100644 --- a/src/supplicant-manager/nm-supplicant-manager.c +++ b/src/supplicant-manager/nm-supplicant-manager.c @@ -27,6 +27,7 @@ #include "nm-supplicant-interface.h" #include "nm-dbus-manager.h" #include "nm-logging.h" +#include "nm-dbus-glib-types.h" #define NM_SUPPLICANT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ NM_TYPE_SUPPLICANT_MANAGER, \ @@ -49,6 +50,7 @@ typedef struct { gboolean running; GHashTable * ifaces; gboolean fast_supported; + ApSupport ap_support; guint die_count_reset_id; guint die_count; gboolean disposed; @@ -86,7 +88,12 @@ nm_supplicant_manager_iface_get (NMSupplicantManager * self, start_now = !die_count_exceeded (priv->die_count); nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname); - iface = nm_supplicant_interface_new (self, ifname, is_wireless, priv->fast_supported, start_now); + iface = nm_supplicant_interface_new (self, + ifname, + is_wireless, + priv->fast_supported, + priv->ap_support, + start_now); if (iface) g_hash_table_insert (priv->ifaces, g_strdup (ifname), iface); } else { @@ -125,52 +132,78 @@ nm_supplicant_manager_iface_release (NMSupplicantManager *self, } static void -get_eap_methods_reply (DBusGProxy *proxy, - DBusGProxyCall *call, - gpointer user_data) +get_capabilities_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) { NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data); NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + NMSupplicantInterface *iface; + GHashTableIter hash_iter; GError *error = NULL; - GValue value = { 0 }; - const char **iter; + GHashTable *props = NULL; + GValue *value; + char **iter; - if (dbus_g_proxy_end_call (proxy, call, &error, - G_TYPE_VALUE, &value, - G_TYPE_INVALID)) { - if (G_VALUE_HOLDS (&value, G_TYPE_STRV)) { - iter = g_value_get_boxed (&value); - while (iter && *iter) { - if (strcasecmp (*iter++, "FAST") == 0) { - priv->fast_supported = TRUE; - break; - } - } - } else { - nm_log_warn (LOGD_SUPPLICANT, "Unexpected EapMethods property type %s", - G_VALUE_TYPE_NAME (&value)); - } - g_value_unset (&value); - } else { - nm_log_warn (LOGD_SUPPLICANT, "Unexpected error requesting EapMethods: (%d) %s", + if (!dbus_g_proxy_end_call (proxy, call_id, &error, + DBUS_TYPE_G_MAP_OF_VARIANT, &props, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_CORE, "Unexpected error requesting supplicant properties: (%d) %s", error ? error->code : -1, error && error->message ? error->message : "(unknown)"); g_clear_error (&error); + return; } - nm_log_dbg (LOGD_SUPPLICANT, "EAP-FAST is %ssupported", - priv->fast_supported ? "" : "not "); + /* The supplicant only advertises global capabilities if the following + * commit has been applied: + * + * commit 1634ac0654eba8d458640a115efc0a6cde3bac4d + * Author: Dan Williams + * Date: Sat Sep 29 19:06:30 2012 +0300 + * + * dbus: Add global capabilities property + */ + priv->ap_support = AP_SUPPORT_UNKNOWN; + value = g_hash_table_lookup (props, "Capabilities"); + if (value && G_VALUE_HOLDS (value, G_TYPE_STRV)) { + priv->ap_support = AP_SUPPORT_NO; + for (iter = g_value_get_boxed (value); iter && *iter; iter++) { + if (strcasecmp (*iter, "ap")) + priv->ap_support = AP_SUPPORT_YES; + } + } + + /* Tell all interfaces about results of the AP check */ + g_hash_table_iter_init (&hash_iter, priv->ifaces); + while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &iface)) + nm_supplicant_interface_set_ap_support (iface, priv->ap_support); + + nm_log_dbg (LOGD_SUPPLICANT, "AP mode is %ssupported", + (priv->ap_support == AP_SUPPORT_YES) ? "" : + (priv->ap_support == AP_SUPPORT_NO) ? "not " : " possibly"); + + /* EAP-FAST */ + priv->fast_supported = FALSE; + value = g_hash_table_lookup (props, "EapMethods"); + if (value && G_VALUE_HOLDS (value, G_TYPE_STRV)) { + for (iter = g_value_get_boxed (value); iter && *iter; iter++) { + if (strcasecmp (*iter, "fast")) + priv->fast_supported = TRUE; + } + } + + nm_log_dbg (LOGD_SUPPLICANT, "EAP-FAST is %ssupported", priv->fast_supported ? "" : "not "); + + g_hash_table_unref (props); } static void -check_supported_eap_methods (NMSupplicantManager *self) +check_capabilities (NMSupplicantManager *self) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - dbus_g_proxy_begin_call (priv->props_proxy, "Get", - get_eap_methods_reply, self, NULL, + dbus_g_proxy_begin_call (priv->props_proxy, "GetAll", + get_capabilities_cb, self, NULL, G_TYPE_STRING, WPAS_DBUS_INTERFACE, - G_TYPE_STRING, "EapMethods", G_TYPE_INVALID); } @@ -239,7 +272,7 @@ name_owner_changed (NMDBusManager *dbus_mgr, if (!old_owner_good && new_owner_good) { nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant started"); set_running (self, TRUE); - check_supported_eap_methods (self); + check_capabilities (self); } else if (old_owner_good && !new_owner_good) { nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant stopped"); @@ -307,9 +340,9 @@ nm_supplicant_manager_init (NMSupplicantManager *self) priv->ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - /* Grab list of supported EAP methods */ + /* Check generic supplicant capabilities */ if (priv->running) - check_supported_eap_methods (self); + check_capabilities (self); } static void