diff options
Diffstat (limited to 'net/wireless/sme.c')
-rw-r--r-- | net/wireless/sme.c | 104 |
1 files changed, 88 insertions, 16 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8a7dcbf90602..8e2ef54ea714 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -27,10 +27,10 @@ struct cfg80211_conn { | |||
27 | CFG80211_CONN_ASSOCIATE_NEXT, | 27 | CFG80211_CONN_ASSOCIATE_NEXT, |
28 | CFG80211_CONN_ASSOCIATING, | 28 | CFG80211_CONN_ASSOCIATING, |
29 | } state; | 29 | } state; |
30 | u8 bssid[ETH_ALEN]; | 30 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; |
31 | u8 *ie; | 31 | u8 *ie; |
32 | size_t ie_len; | 32 | size_t ie_len; |
33 | bool auto_auth; | 33 | bool auto_auth, prev_bssid_valid; |
34 | }; | 34 | }; |
35 | 35 | ||
36 | 36 | ||
@@ -65,7 +65,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) | |||
65 | if (!request) | 65 | if (!request) |
66 | return -ENOMEM; | 66 | return -ENOMEM; |
67 | 67 | ||
68 | request->channels = (void *)((char *)request + sizeof(*request)); | ||
69 | if (wdev->conn->params.channel) | 68 | if (wdev->conn->params.channel) |
70 | request->channels[0] = wdev->conn->params.channel; | 69 | request->channels[0] = wdev->conn->params.channel; |
71 | else { | 70 | else { |
@@ -82,7 +81,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) | |||
82 | } | 81 | } |
83 | } | 82 | } |
84 | request->n_channels = n_channels; | 83 | request->n_channels = n_channels; |
85 | request->ssids = (void *)(request->channels + n_channels); | 84 | request->ssids = (void *)&request->channels[n_channels]; |
86 | request->n_ssids = 1; | 85 | request->n_ssids = 1; |
87 | 86 | ||
88 | memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, | 87 | memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, |
@@ -110,6 +109,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
110 | { | 109 | { |
111 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 110 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
112 | struct cfg80211_connect_params *params; | 111 | struct cfg80211_connect_params *params; |
112 | const u8 *prev_bssid = NULL; | ||
113 | int err; | 113 | int err; |
114 | 114 | ||
115 | ASSERT_WDEV_LOCK(wdev); | 115 | ASSERT_WDEV_LOCK(wdev); |
@@ -135,15 +135,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
135 | case CFG80211_CONN_ASSOCIATE_NEXT: | 135 | case CFG80211_CONN_ASSOCIATE_NEXT: |
136 | BUG_ON(!rdev->ops->assoc); | 136 | BUG_ON(!rdev->ops->assoc); |
137 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; | 137 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; |
138 | /* | 138 | if (wdev->conn->prev_bssid_valid) |
139 | * We could, later, implement roaming here and then actually | 139 | prev_bssid = wdev->conn->prev_bssid; |
140 | * set prev_bssid to non-NULL. But then we need to be aware | ||
141 | * that some APs don't like that -- so we'd need to retry | ||
142 | * the association. | ||
143 | */ | ||
144 | err = __cfg80211_mlme_assoc(rdev, wdev->netdev, | 140 | err = __cfg80211_mlme_assoc(rdev, wdev->netdev, |
145 | params->channel, params->bssid, | 141 | params->channel, params->bssid, |
146 | NULL, | 142 | prev_bssid, |
147 | params->ssid, params->ssid_len, | 143 | params->ssid, params->ssid_len, |
148 | params->ie, params->ie_len, | 144 | params->ie, params->ie_len, |
149 | false, ¶ms->crypto); | 145 | false, ¶ms->crypto); |
@@ -256,9 +252,11 @@ void cfg80211_sme_scan_done(struct net_device *dev) | |||
256 | { | 252 | { |
257 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 253 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
258 | 254 | ||
255 | mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); | ||
259 | wdev_lock(wdev); | 256 | wdev_lock(wdev); |
260 | __cfg80211_sme_scan_done(dev); | 257 | __cfg80211_sme_scan_done(dev); |
261 | wdev_unlock(wdev); | 258 | wdev_unlock(wdev); |
259 | mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); | ||
262 | } | 260 | } |
263 | 261 | ||
264 | void cfg80211_sme_rx_auth(struct net_device *dev, | 262 | void cfg80211_sme_rx_auth(struct net_device *dev, |
@@ -314,6 +312,28 @@ void cfg80211_sme_rx_auth(struct net_device *dev, | |||
314 | } | 312 | } |
315 | } | 313 | } |
316 | 314 | ||
315 | bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) | ||
316 | { | ||
317 | struct wiphy *wiphy = wdev->wiphy; | ||
318 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
319 | |||
320 | if (WARN_ON(!wdev->conn)) | ||
321 | return false; | ||
322 | |||
323 | if (!wdev->conn->prev_bssid_valid) | ||
324 | return false; | ||
325 | |||
326 | /* | ||
327 | * Some stupid APs don't accept reassoc, so we | ||
328 | * need to fall back to trying regular assoc. | ||
329 | */ | ||
330 | wdev->conn->prev_bssid_valid = false; | ||
331 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | ||
332 | schedule_work(&rdev->conn_work); | ||
333 | |||
334 | return true; | ||
335 | } | ||
336 | |||
317 | void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | 337 | void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, |
318 | const u8 *req_ie, size_t req_ie_len, | 338 | const u8 *req_ie, size_t req_ie_len, |
319 | const u8 *resp_ie, size_t resp_ie_len, | 339 | const u8 *resp_ie, size_t resp_ie_len, |
@@ -357,8 +377,11 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
357 | 377 | ||
358 | memset(&wrqu, 0, sizeof(wrqu)); | 378 | memset(&wrqu, 0, sizeof(wrqu)); |
359 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | 379 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
360 | if (bssid && status == WLAN_STATUS_SUCCESS) | 380 | if (bssid && status == WLAN_STATUS_SUCCESS) { |
361 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | 381 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); |
382 | memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); | ||
383 | wdev->wext.prev_bssid_valid = true; | ||
384 | } | ||
362 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 385 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); |
363 | } | 386 | } |
364 | #endif | 387 | #endif |
@@ -509,6 +532,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, | |||
509 | memset(&wrqu, 0, sizeof(wrqu)); | 532 | memset(&wrqu, 0, sizeof(wrqu)); |
510 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | 533 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
511 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | 534 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); |
535 | memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); | ||
536 | wdev->wext.prev_bssid_valid = true; | ||
512 | wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); | 537 | wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); |
513 | #endif | 538 | #endif |
514 | } | 539 | } |
@@ -570,10 +595,30 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
570 | wdev->ssid_len = 0; | 595 | wdev->ssid_len = 0; |
571 | 596 | ||
572 | if (wdev->conn) { | 597 | if (wdev->conn) { |
598 | const u8 *bssid; | ||
599 | int ret; | ||
600 | |||
573 | kfree(wdev->conn->ie); | 601 | kfree(wdev->conn->ie); |
574 | wdev->conn->ie = NULL; | 602 | wdev->conn->ie = NULL; |
575 | kfree(wdev->conn); | 603 | kfree(wdev->conn); |
576 | wdev->conn = NULL; | 604 | wdev->conn = NULL; |
605 | |||
606 | /* | ||
607 | * If this disconnect was due to a disassoc, we | ||
608 | * we might still have an auth BSS around. For | ||
609 | * the userspace SME that's currently expected, | ||
610 | * but for the kernel SME (nl80211 CONNECT or | ||
611 | * wireless extensions) we want to clear up all | ||
612 | * state. | ||
613 | */ | ||
614 | for (i = 0; i < MAX_AUTH_BSSES; i++) { | ||
615 | if (!wdev->auth_bsses[i]) | ||
616 | continue; | ||
617 | bssid = wdev->auth_bsses[i]->pub.bssid; | ||
618 | ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, | ||
619 | WLAN_REASON_DEAUTH_LEAVING); | ||
620 | WARN(ret, "deauth failed: %d\n", ret); | ||
621 | } | ||
577 | } | 622 | } |
578 | 623 | ||
579 | nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); | 624 | nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); |
@@ -621,9 +666,11 @@ EXPORT_SYMBOL(cfg80211_disconnected); | |||
621 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, | 666 | int __cfg80211_connect(struct cfg80211_registered_device *rdev, |
622 | struct net_device *dev, | 667 | struct net_device *dev, |
623 | struct cfg80211_connect_params *connect, | 668 | struct cfg80211_connect_params *connect, |
624 | struct cfg80211_cached_keys *connkeys) | 669 | struct cfg80211_cached_keys *connkeys, |
670 | const u8 *prev_bssid) | ||
625 | { | 671 | { |
626 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 672 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
673 | struct ieee80211_channel *chan; | ||
627 | int err; | 674 | int err; |
628 | 675 | ||
629 | ASSERT_WDEV_LOCK(wdev); | 676 | ASSERT_WDEV_LOCK(wdev); |
@@ -631,6 +678,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
631 | if (wdev->sme_state != CFG80211_SME_IDLE) | 678 | if (wdev->sme_state != CFG80211_SME_IDLE) |
632 | return -EALREADY; | 679 | return -EALREADY; |
633 | 680 | ||
681 | chan = rdev_fixed_channel(rdev, wdev); | ||
682 | if (chan && chan != connect->channel) | ||
683 | return -EBUSY; | ||
684 | |||
634 | if (WARN_ON(wdev->connect_keys)) { | 685 | if (WARN_ON(wdev->connect_keys)) { |
635 | kfree(wdev->connect_keys); | 686 | kfree(wdev->connect_keys); |
636 | wdev->connect_keys = NULL; | 687 | wdev->connect_keys = NULL; |
@@ -638,14 +689,28 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
638 | 689 | ||
639 | if (connkeys && connkeys->def >= 0) { | 690 | if (connkeys && connkeys->def >= 0) { |
640 | int idx; | 691 | int idx; |
692 | u32 cipher; | ||
641 | 693 | ||
642 | idx = connkeys->def; | 694 | idx = connkeys->def; |
695 | cipher = connkeys->params[idx].cipher; | ||
643 | /* If given a WEP key we may need it for shared key auth */ | 696 | /* If given a WEP key we may need it for shared key auth */ |
644 | if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 || | 697 | if (cipher == WLAN_CIPHER_SUITE_WEP40 || |
645 | connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) { | 698 | cipher == WLAN_CIPHER_SUITE_WEP104) { |
646 | connect->key_idx = idx; | 699 | connect->key_idx = idx; |
647 | connect->key = connkeys->params[idx].key; | 700 | connect->key = connkeys->params[idx].key; |
648 | connect->key_len = connkeys->params[idx].key_len; | 701 | connect->key_len = connkeys->params[idx].key_len; |
702 | |||
703 | /* | ||
704 | * If ciphers are not set (e.g. when going through | ||
705 | * iwconfig), we have to set them appropriately here. | ||
706 | */ | ||
707 | if (connect->crypto.cipher_group == 0) | ||
708 | connect->crypto.cipher_group = cipher; | ||
709 | |||
710 | if (connect->crypto.n_ciphers_pairwise == 0) { | ||
711 | connect->crypto.n_ciphers_pairwise = 1; | ||
712 | connect->crypto.ciphers_pairwise[0] = cipher; | ||
713 | } | ||
649 | } | 714 | } |
650 | } | 715 | } |
651 | 716 | ||
@@ -701,6 +766,11 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
701 | wdev->sme_state = CFG80211_SME_CONNECTING; | 766 | wdev->sme_state = CFG80211_SME_CONNECTING; |
702 | wdev->connect_keys = connkeys; | 767 | wdev->connect_keys = connkeys; |
703 | 768 | ||
769 | if (prev_bssid) { | ||
770 | memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); | ||
771 | wdev->conn->prev_bssid_valid = true; | ||
772 | } | ||
773 | |||
704 | /* we're good if we have both BSSID and channel */ | 774 | /* we're good if we have both BSSID and channel */ |
705 | if (wdev->conn->params.bssid && wdev->conn->params.channel) { | 775 | if (wdev->conn->params.bssid && wdev->conn->params.channel) { |
706 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | 776 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; |
@@ -751,9 +821,11 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
751 | { | 821 | { |
752 | int err; | 822 | int err; |
753 | 823 | ||
824 | mutex_lock(&rdev->devlist_mtx); | ||
754 | wdev_lock(dev->ieee80211_ptr); | 825 | wdev_lock(dev->ieee80211_ptr); |
755 | err = __cfg80211_connect(rdev, dev, connect, connkeys); | 826 | err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); |
756 | wdev_unlock(dev->ieee80211_ptr); | 827 | wdev_unlock(dev->ieee80211_ptr); |
828 | mutex_unlock(&rdev->devlist_mtx); | ||
757 | 829 | ||
758 | return err; | 830 | return err; |
759 | } | 831 | } |