Commit ceca7b71 authored by Johannes Berg's avatar Johannes Berg

cfg80211: separate internal SME implementation

The current internal SME implementation in cfg80211 is
very mixed up with the MLME handling, which has been
causing issues for a long time. There are three things
that the implementation has to provide:
 * a basic SME implementation for nl80211's connect()
   call (for drivers implementing auth/assoc, which is
   really just mac80211) and wireless extensions
 * MLME events for the userspace SME
 * SME events (connected, disconnected etc.) for all
   different SME implementation possibilities (driver,
   cfg80211 and userspace)

To achieve these goals it isn't necessary to track the
software SME's connection status outside of it's state
(which is the part that caused many issues.) Instead,
track it only in the SME data (wdev->conn) and in the
general case only track whether the wdev is connected
or not (via wdev->current_bss.)

Also separate the internal implementation to not have
callbacks from the SME events, but rather call it from
the API functions that the driver (or rather mac80211)
calls. This separates the code better.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6ff57cf8
......@@ -2898,11 +2898,6 @@ struct wireless_dev {
/* currently used for IBSS and SME - might be rearranged later */
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len, mesh_id_len, mesh_id_up_len;
enum {
CFG80211_SME_IDLE,
CFG80211_SME_CONNECTING,
CFG80211_SME_CONNECTED,
} sme_state;
struct cfg80211_conn *conn;
struct cfg80211_cached_keys *connect_keys;
......
......@@ -821,7 +821,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
pr_err("failed to add phy80211 symlink to netdev!\n");
}
wdev->netdev = dev;
wdev->sme_state = CFG80211_SME_IDLE;
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
wdev->wext.default_mgmt_key = -1;
......
......@@ -308,11 +308,6 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
bool local_state_change);
void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
struct net_device *dev);
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
u16 status, bool wextev,
struct cfg80211_bss *bss);
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
u16 frame_type, const u8 *match_data,
int match_len);
......@@ -328,12 +323,19 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
const struct ieee80211_vht_cap *vht_capa_mask);
/* SME */
/* SME events */
int cfg80211_connect(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_connect_params *connect,
struct cfg80211_cached_keys *connkeys,
const u8 *prev_bssid);
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
u16 status, bool wextev,
struct cfg80211_bss *bss);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap);
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
struct net_device *dev, u16 reason,
bool wextev);
......@@ -344,21 +346,21 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
/* SME implementation */
void cfg80211_conn_work(struct work_struct *work);
void cfg80211_sme_failed_assoc(struct wireless_dev *wdev);
bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev);
void cfg80211_sme_scan_done(struct net_device *dev);
bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
void cfg80211_sme_disassoc(struct wireless_dev *wdev);
void cfg80211_sme_deauth(struct wireless_dev *wdev);
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
/* internal helpers */
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct key_params *params, int key_idx,
bool pairwise, const u8 *mac_addr);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap);
void cfg80211_sme_scan_done(struct net_device *dev);
void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
void cfg80211_sme_disassoc(struct net_device *dev,
struct cfg80211_internal_bss *bss);
void __cfg80211_scan_done(struct work_struct *wk);
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
void __cfg80211_sched_scan_results(struct work_struct *wk);
......
......@@ -43,7 +43,6 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
cfg80211_hold_bss(bss_from_pub(bss));
wdev->current_bss = bss_from_pub(bss);
wdev->sme_state = CFG80211_SME_CONNECTED;
cfg80211_upload_connect_keys(wdev);
nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
......@@ -64,8 +63,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
trace_cfg80211_ibss_joined(dev, bssid);
CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
ev = kzalloc(sizeof(*ev), gfp);
if (!ev)
return;
......@@ -120,7 +117,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.ibss.chandef = params->chandef;
#endif
wdev->sme_state = CFG80211_SME_CONNECTING;
err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
params->channel_fixed
......@@ -134,7 +130,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
err = rdev_join_ibss(rdev, dev, params);
if (err) {
wdev->connect_keys = NULL;
wdev->sme_state = CFG80211_SME_IDLE;
return err;
}
......@@ -186,7 +181,6 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
}
wdev->current_bss = NULL;
wdev->sme_state = CFG80211_SME_IDLE;
wdev->ssid_len = 0;
#ifdef CONFIG_CFG80211_WEXT
if (!nowext)
......
......@@ -21,129 +21,85 @@
void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
const u8 *buf, size_t len)
{
u16 status_code;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
u8 *ie = mgmt->u.assoc_resp.variable;
int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
trace_cfg80211_send_rx_assoc(dev, bss);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
/*
* This is a bit of a hack, we don't notify userspace of
* a (re-)association reply if we tried to send a reassoc
* and got a reject -- we only try again with an assoc
* frame instead of reassoc.
*/
if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
cfg80211_sme_failed_reassoc(wdev)) {
if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
cfg80211_put_bss(wiphy, bss);
return;
}
nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
cfg80211_sme_failed_assoc(wdev);
/*
* do not call connect_result() now because the
* sme will schedule work that does it later.
*/
cfg80211_put_bss(wiphy, bss);
return;
}
if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
/*
* This is for the userspace SME, the CONNECTING
* state will be changed to CONNECTED by
* __cfg80211_connect_result() below.
*/
wdev->sme_state = CFG80211_SME_CONNECTING;
}
/* this consumes the bss reference */
/* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
status_code,
status_code == WLAN_STATUS_SUCCESS, bss);
}
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
static void cfg80211_process_deauth(struct net_device *dev,
static void cfg80211_process_auth(struct wireless_dev *wdev,
const u8 *buf, size_t len)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
cfg80211_sme_rx_auth(wdev, buf, len);
}
static void cfg80211_process_deauth(struct wireless_dev *wdev,
const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
const u8 *bssid = mgmt->bssid;
bool was_current = false;
if (wdev->current_bss &&
ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
was_current = true;
}
u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
u16 reason_code;
bool from_ap;
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
if (!wdev->current_bss ||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
return;
from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
} else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
false, NULL);
}
__cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
cfg80211_sme_deauth(wdev);
}
static void cfg80211_process_disassoc(struct net_device *dev,
static void cfg80211_process_disassoc(struct wireless_dev *wdev,
const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
const u8 *bssid = mgmt->bssid;
u16 reason_code;
bool from_ap;
u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
if (wdev->sme_state != CFG80211_SME_CONNECTED)
if (WARN_ON(!wdev->current_bss ||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
return;
if (wdev->current_bss &&
ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
cfg80211_sme_disassoc(dev, wdev->current_bss);
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
} else
WARN_ON(1);
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
__cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
cfg80211_sme_disassoc(wdev);
}
void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct ieee80211_mgmt *mgmt = (void *)buf;
ASSERT_WDEV_LOCK(wdev);
......@@ -153,14 +109,12 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
if (WARN_ON(len < 2))
return;
if (ieee80211_is_auth(mgmt->frame_control)) {
nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
cfg80211_sme_rx_auth(dev, buf, len);
} else if (ieee80211_is_deauth(mgmt->frame_control)) {
cfg80211_process_deauth(dev, buf, len);
} else if (ieee80211_is_disassoc(mgmt->frame_control)) {
cfg80211_process_disassoc(dev, buf, len);
}
if (ieee80211_is_auth(mgmt->frame_control))
cfg80211_process_auth(wdev, buf, len);
else if (ieee80211_is_deauth(mgmt->frame_control))
cfg80211_process_deauth(wdev, buf, len);
else if (ieee80211_is_disassoc(mgmt->frame_control))
cfg80211_process_disassoc(wdev, buf, len);
}
EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
......@@ -173,10 +127,7 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
trace_cfg80211_send_auth_timeout(dev, addr);
nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
if (wdev->sme_state == CFG80211_SME_CONNECTING)
__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
false, NULL);
cfg80211_sme_auth_timeout(wdev);
}
EXPORT_SYMBOL(cfg80211_auth_timeout);
......@@ -189,10 +140,7 @@ void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr)
trace_cfg80211_send_assoc_timeout(dev, addr);
nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
if (wdev->sme_state == CFG80211_SME_CONNECTING)
__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
false, NULL);
cfg80211_sme_assoc_timeout(wdev);
}
EXPORT_SYMBOL(cfg80211_assoc_timeout);
......@@ -209,9 +157,9 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
return;
if (ieee80211_is_deauth(mgmt->frame_control))
cfg80211_process_deauth(dev, buf, len);
cfg80211_process_deauth(wdev, buf, len);
else
cfg80211_process_disassoc(dev, buf, len);
cfg80211_process_disassoc(wdev, buf, len);
}
EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
......@@ -336,21 +284,12 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
bool was_connected = false;
ASSERT_WDEV_LOCK(wdev);
if (wdev->current_bss && req->prev_bssid &&
ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
/*
* Trying to reassociate: Allow this to proceed and let the old
* association to be dropped when the new one is completed.
*/
if (wdev->sme_state == CFG80211_SME_CONNECTED) {
was_connected = true;
wdev->sme_state = CFG80211_SME_CONNECTING;
}
} else if (wdev->current_bss)
if (wdev->current_bss &&
(!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
req->prev_bssid)))
return -EALREADY;
cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
......@@ -360,11 +299,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (!req->bss) {
if (was_connected)
wdev->sme_state = CFG80211_SME_CONNECTED;
if (!req->bss)
return -ENOENT;
}
err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
if (err)
......@@ -373,11 +309,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
err = rdev_assoc(rdev, dev, req);
out:
if (err) {
if (was_connected)
wdev->sme_state = CFG80211_SME_CONNECTED;
if (err)
cfg80211_put_bss(&rdev->wiphy, req->bss);
}
return err;
}
......@@ -398,8 +331,9 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
ASSERT_WDEV_LOCK(wdev);
if (local_state_change && (!wdev->current_bss ||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
if (local_state_change &&
(!wdev->current_bss ||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
return 0;
return rdev_deauth(rdev, dev, &req);
......@@ -417,13 +351,11 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
.ie = ie,
.ie_len = ie_len,
};
int err;
ASSERT_WDEV_LOCK(wdev);
if (wdev->sme_state != CFG80211_SME_CONNECTED)
return -ENOTCONN;
if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
if (!wdev->current_bss)
return -ENOTCONN;
if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
......@@ -431,7 +363,13 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
else
return -ENOTCONN;
return rdev_disassoc(rdev, dev, &req);
err = rdev_disassoc(rdev, dev, &req);
if (err)
return err;
/* driver should have reported the disassoc */
WARN_ON(wdev->current_bss);
return 0;
}
void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
......@@ -439,10 +377,6 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
u8 bssid[ETH_ALEN];
struct cfg80211_deauth_request req = {
.reason_code = WLAN_REASON_DEAUTH_LEAVING,
.bssid = bssid,
};
ASSERT_WDEV_LOCK(wdev);
......@@ -453,13 +387,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
return;
memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
rdev_deauth(rdev, dev, &req);
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
}
cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
WLAN_REASON_DEAUTH_LEAVING, false);
}
struct cfg80211_mgmt_registration {
......
......@@ -800,12 +800,9 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
case NL80211_IFTYPE_MESH_POINT:
break;
case NL80211_IFTYPE_ADHOC:
if (!wdev->current_bss)
return -ENOLINK;
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (wdev->sme_state != CFG80211_SME_CONNECTED)
if (!wdev->current_bss)
return -ENOLINK;
break;
default:
......
This diff is collapsed.
......@@ -89,7 +89,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
wdev_lock(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE) {
if (wdev->conn) {
bool event = true;
if (wdev->wext.connect.channel == chan) {
......@@ -188,7 +188,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
err = 0;
if (wdev->sme_state != CFG80211_SME_IDLE) {
if (wdev->conn) {
bool event = true;
if (wdev->wext.connect.ssid && len &&
......@@ -277,7 +277,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
wdev_lock(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE) {
if (wdev->conn) {
err = 0;
/* both automatic */
if (!bssid && !wdev->wext.connect.bssid)
......@@ -364,7 +364,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
wdev->wext.ie = ie;
wdev->wext.ie_len = ie_len;
if (wdev->sme_state != CFG80211_SME_IDLE) {
if (wdev->conn) {
err = cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, false);
if (err)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment