cfg80211: send extended capabilities IE in connect

If the connect request from userspace didn't include an extended
capabilities IE, create one using the driver capabilities. This
fixes VHT associations, since those need to set the operating mode
notification capability.

Reviewed-by: Gregory Greenman <gregory.greenman@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2015-03-31 16:09:13 +02:00
parent 29464ccc78
commit 46b9d18014
1 changed files with 64 additions and 10 deletions

View File

@ -42,7 +42,7 @@ struct cfg80211_conn {
CFG80211_CONN_CONNECTED,
} state;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 *ie;
const u8 *ie;
size_t ie_len;
bool auto_auth, prev_bssid_valid;
};
@ -423,6 +423,62 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
schedule_work(&rdev->conn_work);
}
static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
const u8 *ies, size_t ies_len,
const u8 **out_ies, size_t *out_ies_len)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
u8 *buf;
size_t offs;
if (!rdev->wiphy.extended_capabilities_len ||
(ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) {
*out_ies = kmemdup(ies, ies_len, GFP_KERNEL);
if (!*out_ies)
return -ENOMEM;
*out_ies_len = ies_len;
return 0;
}
buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2,
GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (ies_len) {
static const u8 before_extcapa[] = {
/* not listing IEs expected to be created by driver */
WLAN_EID_RSN,
WLAN_EID_QOS_CAPA,
WLAN_EID_RRM_ENABLED_CAPABILITIES,
WLAN_EID_MOBILITY_DOMAIN,
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
WLAN_EID_BSS_COEX_2040,
};
offs = ieee80211_ie_split(ies, ies_len, before_extcapa,
ARRAY_SIZE(before_extcapa), 0);
memcpy(buf, ies, offs);
/* leave a whole for extended capabilities IE */
memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2,
ies + offs, ies_len - offs);
} else {
offs = 0;
}
/* place extended capabilities IE (with only driver capabilities) */
buf[offs] = WLAN_EID_EXT_CAPABILITY;
buf[offs + 1] = rdev->wiphy.extended_capabilities_len;
memcpy(buf + offs + 2,
rdev->wiphy.extended_capabilities,
rdev->wiphy.extended_capabilities_len);
*out_ies = buf;
*out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2;
return 0;
}
static int cfg80211_sme_connect(struct wireless_dev *wdev,
struct cfg80211_connect_params *connect,
const u8 *prev_bssid)
@ -453,16 +509,14 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
}
if (connect->ie) {
wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
GFP_KERNEL);
wdev->conn->params.ie = wdev->conn->ie;
if (!wdev->conn->ie) {
kfree(wdev->conn);
wdev->conn = NULL;
return -ENOMEM;
}
if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len,
&wdev->conn->ie,
&wdev->conn->params.ie_len)) {
kfree(wdev->conn);
wdev->conn = NULL;
return -ENOMEM;
}
wdev->conn->params.ie = wdev->conn->ie;
if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
wdev->conn->auto_auth = true;