mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-06 12:50:33 +01:00
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:
parent
323ff852ba
commit
b89990ae2c
5 changed files with 188 additions and 41 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue