wifi: detect whether supplicant supports AP mode or not

And return an error when trying to activate an AP mode connection
if the supplicant does not support it.

With wpa_supplicant 1.0 and earlier there is no way to positively detect
whether AP mode is supported, so we simply try to start AP mode
and then fail if it doesn't work.

With more recent versions we can check the Introspection data
(if the supplicant has been built with introspection enabled) or
check the global Capabilities (if the supplicant is recent enough)
for positive indication of AP mode support.
This commit is contained in:
Dan Williams 2012-09-18 17:33:57 -05:00
parent 323ff852ba
commit b89990ae2c
5 changed files with 188 additions and 41 deletions

View file

@ -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),

View file

@ -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"

View file

@ -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);

View file

@ -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 */

View file

@ -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 <dcbw@redhat.com>
* 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