diff options
-rw-r--r-- | include/net/cfg80211.h | 4 | ||||
-rw-r--r-- | net/wireless/core.h | 4 | ||||
-rw-r--r-- | net/wireless/mlme.c | 11 | ||||
-rw-r--r-- | net/wireless/sme.c | 54 | ||||
-rw-r--r-- | net/wireless/wext-sme.c | 8 |
5 files changed, 65 insertions, 16 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 64df51d9a89f..1ee30fcd6fdc 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1335,10 +1335,10 @@ struct wireless_dev { struct cfg80211_cached_keys *keys; u8 *ie; size_t ie_len; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 ssid[IEEE80211_MAX_SSID_LEN]; s8 default_key, default_mgmt_key; - bool ps; + bool ps, prev_bssid_valid; int ps_timeout; } wext; #endif diff --git a/net/wireless/core.h b/net/wireless/core.h index 5696b95af9be..92e0492b0e4d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -335,7 +335,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys); + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, @@ -353,6 +354,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_conn_work(struct work_struct *work); +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); /* internal helpers */ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 51d5df67c632..da64071ceb84 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -67,6 +67,16 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) 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)) + goto out; + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); if (status_code == WLAN_STATUS_SUCCESS) { @@ -97,6 +107,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) cfg80211_put_bss(&bss->pub); } + out: wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 219c3bc2c37d..104b33e34d22 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -27,10 +27,10 @@ struct cfg80211_conn { CFG80211_CONN_ASSOCIATE_NEXT, CFG80211_CONN_ASSOCIATING, } state; - u8 bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; u8 *ie; size_t ie_len; - bool auto_auth; + bool auto_auth, prev_bssid_valid; }; @@ -110,6 +110,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_connect_params *params; + const u8 *prev_bssid = NULL; int err; ASSERT_WDEV_LOCK(wdev); @@ -135,15 +136,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; - /* - * We could, later, implement roaming here and then actually - * set prev_bssid to non-NULL. But then we need to be aware - * that some APs don't like that -- so we'd need to retry - * the association. - */ + if (wdev->conn->prev_bssid_valid) + prev_bssid = wdev->conn->prev_bssid; err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, params->bssid, - NULL, + prev_bssid, params->ssid, params->ssid_len, params->ie, params->ie_len, false, ¶ms->crypto); @@ -316,6 +313,28 @@ void cfg80211_sme_rx_auth(struct net_device *dev, } } +bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) +{ + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!wdev->conn)) + return false; + + if (!wdev->conn->prev_bssid_valid) + return false; + + /* + * Some stupid APs don't accept reassoc, so we + * need to fall back to trying regular assoc. + */ + wdev->conn->prev_bssid_valid = false; + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + + return true; +} + 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, @@ -359,8 +378,11 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) + if (bssid && status == WLAN_STATUS_SUCCESS) { memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; + } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif @@ -511,6 +533,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif } @@ -643,7 +667,8 @@ EXPORT_SYMBOL(cfg80211_disconnected); int __cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys) + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_channel *chan; @@ -742,6 +767,11 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, wdev->sme_state = CFG80211_SME_CONNECTING; wdev->connect_keys = connkeys; + if (prev_bssid) { + memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); + wdev->conn->prev_bssid_valid = true; + } + /* we're good if we have both BSSID and channel */ if (wdev->conn->params.bssid && wdev->conn->params.channel) { wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; @@ -794,7 +824,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect, connkeys); + err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); mutex_unlock(&rdev->devlist_mtx); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index fe1a53639122..907470063f22 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -15,6 +15,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct cfg80211_cached_keys *ck = NULL; + const u8 *prev_bssid = NULL; int err, i; ASSERT_RDEV_LOCK(rdev); @@ -42,8 +43,12 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, for (i = 0; i < 6; i++) ck->params[i].key = ck->data[i]; } + + if (wdev->wext.prev_bssid_valid) + prev_bssid = wdev->wext.prev_bssid; + err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect, ck); + &wdev->wext.connect, ck, prev_bssid); if (err) kfree(ck); @@ -184,6 +189,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, goto out; } + wdev->wext.prev_bssid_valid = false; wdev->wext.connect.ssid = wdev->wext.ssid; memcpy(wdev->wext.ssid, ssid, len); wdev->wext.connect.ssid_len = len; |