aboutsummaryrefslogtreecommitdiff
path: root/net/wireless/mlme.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/mlme.c')
-rw-r--r--net/wireless/mlme.c357
1 files changed, 342 insertions, 15 deletions
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 3427fe73d3c..1a92bf7597b 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -14,8 +14,32 @@
void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp)
{
- struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ 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 *bssid = mgmt->bssid;
+ int i;
+ u16 status = le16_to_cpu(mgmt->u.auth.status_code);
+ bool done = false;
+
+ for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (wdev->authtry_bsses[i] &&
+ memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
+ ETH_ALEN) == 0) {
+ if (status == WLAN_STATUS_SUCCESS) {
+ wdev->auth_bsses[i] = wdev->authtry_bsses[i];
+ } else {
+ cfg80211_unhold_bss(wdev->authtry_bsses[i]);
+ cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
+ }
+ wdev->authtry_bsses[i] = NULL;
+ done = true;
+ break;
+ }
+ }
+
+ WARN_ON(!done);
nl80211_send_rx_auth(rdev, dev, buf, len, gfp);
cfg80211_sme_rx_auth(dev, buf, len);
@@ -30,7 +54,8 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g
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);
+ int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+ bool done;
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
@@ -38,6 +63,20 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, g
cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
status_code, gfp);
+
+ if (status_code == WLAN_STATUS_SUCCESS) {
+ for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) {
+ if (wdev->auth_bsses[i] == wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->auth_bsses[i]);
+ cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
+ wdev->auth_bsses[i] = NULL;
+ done = true;
+ break;
+ }
+ }
+
+ WARN_ON(!done);
+ }
}
EXPORT_SYMBOL(cfg80211_send_rx_assoc);
@@ -47,9 +86,45 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+ const u8 *bssid = mgmt->bssid;
+ int i;
+ bool done = false;
nl80211_send_deauth(rdev, dev, buf, len, gfp);
+ if (wdev->current_bss &&
+ memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
+ done = true;
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(&wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+ } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (wdev->auth_bsses[i] &&
+ memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
+ cfg80211_unhold_bss(wdev->auth_bsses[i]);
+ cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
+ wdev->auth_bsses[i] = NULL;
+ done = true;
+ break;
+ }
+ if (wdev->authtry_bsses[i] &&
+ memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
+ cfg80211_unhold_bss(wdev->authtry_bsses[i]);
+ cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
+ wdev->authtry_bsses[i] = NULL;
+ done = true;
+ break;
+ }
+ }
+/*
+ * mac80211 currently triggers this warning,
+ * so disable for now (it's harmless, just
+ * means that we got a spurious event)
+
+ WARN_ON(!done);
+
+ */
+
if (wdev->sme_state == CFG80211_SME_CONNECTED) {
u16 reason_code;
bool from_ap;
@@ -59,8 +134,6 @@ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp
from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
__cfg80211_disconnected(dev, gfp, NULL, 0,
reason_code, from_ap);
-
- wdev->sme_state = CFG80211_SME_IDLE;
} else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
@@ -74,21 +147,38 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, g
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+ const u8 *bssid = mgmt->bssid;
+ int i;
+ u16 reason_code;
+ bool from_ap;
+ bool done = false;
nl80211_send_disassoc(rdev, dev, buf, len, gfp);
- if (wdev->sme_state == CFG80211_SME_CONNECTED) {
- u16 reason_code;
- bool from_ap;
+ if (!wdev->sme_state == CFG80211_SME_CONNECTED)
+ return;
- reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+ if (wdev->current_bss &&
+ memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) {
+ for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
+ continue;
+ wdev->auth_bsses[i] = wdev->current_bss;
+ wdev->current_bss = NULL;
+ done = true;
+ cfg80211_sme_disassoc(dev, i);
+ break;
+ }
+ WARN_ON(!done);
+ } else
+ WARN_ON(1);
- from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
- __cfg80211_disconnected(dev, gfp, NULL, 0,
- reason_code, from_ap);
- wdev->sme_state = CFG80211_SME_IDLE;
- }
+ reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+
+ from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0;
+ __cfg80211_disconnected(dev, gfp, NULL, 0,
+ reason_code, from_ap);
}
EXPORT_SYMBOL(cfg80211_send_disassoc);
@@ -97,11 +187,27 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gf
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ int i;
+ bool done = false;
+
nl80211_send_auth_timeout(rdev, dev, addr, gfp);
if (wdev->sme_state == CFG80211_SME_CONNECTING)
cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
- wdev->sme_state = CFG80211_SME_IDLE;
+
+ for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
+ if (wdev->authtry_bsses[i] &&
+ memcmp(wdev->authtry_bsses[i]->pub.bssid,
+ addr, ETH_ALEN) == 0) {
+ cfg80211_unhold_bss(wdev->authtry_bsses[i]);
+ cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
+ wdev->authtry_bsses[i] = NULL;
+ done = true;
+ break;
+ }
+ }
+
+ WARN_ON(!done);
}
EXPORT_SYMBOL(cfg80211_send_auth_timeout);
@@ -110,11 +216,27 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t g
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ int i;
+ bool done = false;
+
nl80211_send_assoc_timeout(rdev, dev, addr, gfp);
if (wdev->sme_state == CFG80211_SME_CONNECTING)
cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, gfp);
- wdev->sme_state = CFG80211_SME_IDLE;
+
+ for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
+ if (wdev->auth_bsses[i] &&
+ memcmp(wdev->auth_bsses[i]->pub.bssid,
+ addr, ETH_ALEN) == 0) {
+ cfg80211_unhold_bss(wdev->auth_bsses[i]);
+ cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
+ wdev->auth_bsses[i] = NULL;
+ done = true;
+ break;
+ }
+ }
+
+ WARN_ON(!done);
}
EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
@@ -143,3 +265,208 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
}
EXPORT_SYMBOL(cfg80211_michael_mic_failure);
+
+/* some MLME handling for userspace SME */
+int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, struct ieee80211_channel *chan,
+ enum nl80211_auth_type auth_type, const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ const u8 *ie, int ie_len)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_auth_request req;
+ struct cfg80211_internal_bss *bss;
+ int i, err, slot = -1, nfree = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.ie = ie;
+ req.ie_len = ie_len;
+ req.auth_type = auth_type;
+ req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+ if (!req.bss)
+ return -ENOENT;
+
+ bss = bss_from_pub(req.bss);
+
+ for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (bss == wdev->auth_bsses[i]) {
+ err = -EALREADY;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
+ slot = i;
+ nfree++;
+ }
+ }
+
+ /* we need one free slot for disassoc and one for this auth */
+ if (nfree < 2) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ wdev->authtry_bsses[slot] = bss;
+ cfg80211_hold_bss(bss);
+
+ err = rdev->ops->auth(&rdev->wiphy, dev, &req);
+ if (err) {
+ wdev->authtry_bsses[slot] = NULL;
+ cfg80211_unhold_bss(bss);
+ }
+
+ out:
+ if (err)
+ cfg80211_put_bss(req.bss);
+ return err;
+}
+
+int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, struct ieee80211_channel *chan,
+ const u8 *bssid, const u8 *ssid, int ssid_len,
+ const u8 *ie, int ie_len, bool use_mfp,
+ struct cfg80211_crypto_settings *crypt)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_assoc_request req;
+ struct cfg80211_internal_bss *bss;
+ int i, err, slot = -1;
+
+ memset(&req, 0, sizeof(req));
+
+ if (wdev->current_bss)
+ return -EALREADY;
+
+ req.ie = ie;
+ req.ie_len = ie_len;
+ memcpy(&req.crypto, crypt, sizeof(req.crypto));
+ req.use_mfp = use_mfp;
+ req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+ if (!req.bss)
+ return -ENOENT;
+
+ bss = bss_from_pub(req.bss);
+
+ for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (bss == wdev->auth_bsses[i]) {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot < 0) {
+ err = -ENOTCONN;
+ goto out;
+ }
+
+ err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
+ out:
+ /* still a reference in wdev->auth_bsses[slot] */
+ cfg80211_put_bss(req.bss);
+ return err;
+}
+
+int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_deauth_request req;
+ int i;
+
+ memset(&req, 0, sizeof(req));
+ req.reason_code = reason;
+ req.ie = ie;
+ req.ie_len = ie_len;
+ if (wdev->current_bss &&
+ memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
+ req.bss = &wdev->current_bss->pub;
+ } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (wdev->auth_bsses[i] &&
+ memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
+ req.bss = &wdev->auth_bsses[i]->pub;
+ break;
+ }
+ if (wdev->authtry_bsses[i] &&
+ memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
+ req.bss = &wdev->authtry_bsses[i]->pub;
+ break;
+ }
+ }
+
+ if (!req.bss)
+ return -ENOTCONN;
+
+ return rdev->ops->deauth(&rdev->wiphy, dev, &req);
+}
+
+int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_disassoc_request req;
+
+ memset(&req, 0, sizeof(req));
+ req.reason_code = reason;
+ req.ie = ie;
+ req.ie_len = ie_len;
+ if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
+ req.bss = &wdev->current_bss->pub;
+ else
+ return -ENOTCONN;
+
+ return rdev->ops->disassoc(&rdev->wiphy, dev, &req);
+}
+
+void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_deauth_request req;
+ int i;
+
+ if (!rdev->ops->deauth)
+ return;
+
+ memset(&req, 0, sizeof(req));
+ req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
+ req.ie = NULL;
+ req.ie_len = 0;
+
+ if (wdev->current_bss) {
+ req.bss = &wdev->current_bss->pub;
+ rdev->ops->deauth(&rdev->wiphy, dev, &req);
+ if (wdev->current_bss) {
+ cfg80211_unhold_bss(wdev->current_bss);
+ cfg80211_put_bss(&wdev->current_bss->pub);
+ wdev->current_bss = NULL;
+ }
+ }
+
+ for (i = 0; i < MAX_AUTH_BSSES; i++) {
+ if (wdev->auth_bsses[i]) {
+ req.bss = &wdev->auth_bsses[i]->pub;
+ rdev->ops->deauth(&rdev->wiphy, dev, &req);
+ if (wdev->auth_bsses[i]) {
+ cfg80211_unhold_bss(wdev->auth_bsses[i]);
+ cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
+ wdev->auth_bsses[i] = NULL;
+ }
+ }
+ if (wdev->authtry_bsses[i]) {
+ req.bss = &wdev->authtry_bsses[i]->pub;
+ rdev->ops->deauth(&rdev->wiphy, dev, &req);
+ if (wdev->authtry_bsses[i]) {
+ cfg80211_unhold_bss(wdev->authtry_bsses[i]);
+ cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
+ wdev->authtry_bsses[i] = NULL;
+ }
+ }
+ }
+}