aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-05-15 18:55:45 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-06-04 07:03:11 -0400
commitceca7b7121795ef81bd598a240d53a925662d0c1 (patch)
tree510d7a6c578d7ede025c8b4714b9584729517b0a /net/wireless/mlme.c
parent6ff57cf88807dd81300b5b9c623dc5eb6422b9f6 (diff)
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: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/mlme.c')
-rw-r--r--net/wireless/mlme.c191
1 files changed, 60 insertions, 131 deletions
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 4b9c2be0d56d..a61a44bc6cf0 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -21,129 +21,85 @@
21void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, 21void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
22 const u8 *buf, size_t len) 22 const u8 *buf, size_t len)
23{ 23{
24 u16 status_code;
25 struct wireless_dev *wdev = dev->ieee80211_ptr; 24 struct wireless_dev *wdev = dev->ieee80211_ptr;
26 struct wiphy *wiphy = wdev->wiphy; 25 struct wiphy *wiphy = wdev->wiphy;
27 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 26 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
28 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 27 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
29 u8 *ie = mgmt->u.assoc_resp.variable; 28 u8 *ie = mgmt->u.assoc_resp.variable;
30 int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); 29 int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
30 u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
31 31
32 trace_cfg80211_send_rx_assoc(dev, bss); 32 trace_cfg80211_send_rx_assoc(dev, bss);
33 33
34 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
35
36 /* 34 /*
37 * This is a bit of a hack, we don't notify userspace of 35 * This is a bit of a hack, we don't notify userspace of
38 * a (re-)association reply if we tried to send a reassoc 36 * a (re-)association reply if we tried to send a reassoc
39 * and got a reject -- we only try again with an assoc 37 * and got a reject -- we only try again with an assoc
40 * frame instead of reassoc. 38 * frame instead of reassoc.
41 */ 39 */
42 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && 40 if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
43 cfg80211_sme_failed_reassoc(wdev)) {
44 cfg80211_put_bss(wiphy, bss); 41 cfg80211_put_bss(wiphy, bss);
45 return; 42 return;
46 } 43 }
47 44
48 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); 45 nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
49 46 /* update current_bss etc., consumes the bss reference */
50 if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
51 cfg80211_sme_failed_assoc(wdev);
52 /*
53 * do not call connect_result() now because the
54 * sme will schedule work that does it later.
55 */
56 cfg80211_put_bss(wiphy, bss);
57 return;
58 }
59
60 if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
61 /*
62 * This is for the userspace SME, the CONNECTING
63 * state will be changed to CONNECTED by
64 * __cfg80211_connect_result() below.
65 */
66 wdev->sme_state = CFG80211_SME_CONNECTING;
67 }
68
69 /* this consumes the bss reference */
70 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, 47 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
71 status_code, 48 status_code,
72 status_code == WLAN_STATUS_SUCCESS, bss); 49 status_code == WLAN_STATUS_SUCCESS, bss);
73} 50}
74EXPORT_SYMBOL(cfg80211_rx_assoc_resp); 51EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
75 52
76static void cfg80211_process_deauth(struct net_device *dev, 53static void cfg80211_process_auth(struct wireless_dev *wdev,
54 const u8 *buf, size_t len)
55{
56 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
57
58 nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
59 cfg80211_sme_rx_auth(wdev, buf, len);
60}
61
62static void cfg80211_process_deauth(struct wireless_dev *wdev,
77 const u8 *buf, size_t len) 63 const u8 *buf, size_t len)
78{ 64{
79 struct wireless_dev *wdev = dev->ieee80211_ptr; 65 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
80 struct wiphy *wiphy = wdev->wiphy;
81 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
82 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 66 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
83 const u8 *bssid = mgmt->bssid; 67 const u8 *bssid = mgmt->bssid;
84 bool was_current = false; 68 u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
85 69 bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
86 if (wdev->current_bss &&
87 ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
88 cfg80211_unhold_bss(wdev->current_bss);
89 cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
90 wdev->current_bss = NULL;
91 was_current = true;
92 }
93 70
94 nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); 71 nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
95 72
96 if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { 73 if (!wdev->current_bss ||
97 u16 reason_code; 74 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
98 bool from_ap; 75 return;
99
100 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
101 76
102 from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr); 77 __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
103 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); 78 cfg80211_sme_deauth(wdev);
104 } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
105 __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
106 WLAN_STATUS_UNSPECIFIED_FAILURE,
107 false, NULL);
108 }
109} 79}
110 80
111static void cfg80211_process_disassoc(struct net_device *dev, 81static void cfg80211_process_disassoc(struct wireless_dev *wdev,
112 const u8 *buf, size_t len) 82 const u8 *buf, size_t len)
113{ 83{
114 struct wireless_dev *wdev = dev->ieee80211_ptr; 84 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
115 struct wiphy *wiphy = wdev->wiphy;
116 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
117 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 85 struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
118 const u8 *bssid = mgmt->bssid; 86 const u8 *bssid = mgmt->bssid;
119 u16 reason_code; 87 u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
120 bool from_ap; 88 bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
121 89
122 nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); 90 nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
123 91
124 if (wdev->sme_state != CFG80211_SME_CONNECTED) 92 if (WARN_ON(!wdev->current_bss ||
93 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
125 return; 94 return;
126 95
127 if (wdev->current_bss && 96 __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
128 ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { 97 cfg80211_sme_disassoc(wdev);
129 cfg80211_sme_disassoc(dev, wdev->current_bss);
130 cfg80211_unhold_bss(wdev->current_bss);
131 cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
132 wdev->current_bss = NULL;
133 } else
134 WARN_ON(1);
135
136 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
137
138 from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
139 __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
140} 98}
141 99
142void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) 100void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
143{ 101{
144 struct wireless_dev *wdev = dev->ieee80211_ptr; 102 struct wireless_dev *wdev = dev->ieee80211_ptr;
145 struct wiphy *wiphy = wdev->wiphy;
146 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
147 struct ieee80211_mgmt *mgmt = (void *)buf; 103 struct ieee80211_mgmt *mgmt = (void *)buf;
148 104
149 ASSERT_WDEV_LOCK(wdev); 105 ASSERT_WDEV_LOCK(wdev);
@@ -153,14 +109,12 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
153 if (WARN_ON(len < 2)) 109 if (WARN_ON(len < 2))
154 return; 110 return;
155 111
156 if (ieee80211_is_auth(mgmt->frame_control)) { 112 if (ieee80211_is_auth(mgmt->frame_control))
157 nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); 113 cfg80211_process_auth(wdev, buf, len);
158 cfg80211_sme_rx_auth(dev, buf, len); 114 else if (ieee80211_is_deauth(mgmt->frame_control))
159 } else if (ieee80211_is_deauth(mgmt->frame_control)) { 115 cfg80211_process_deauth(wdev, buf, len);
160 cfg80211_process_deauth(dev, buf, len); 116 else if (ieee80211_is_disassoc(mgmt->frame_control))
161 } else if (ieee80211_is_disassoc(mgmt->frame_control)) { 117 cfg80211_process_disassoc(wdev, buf, len);
162 cfg80211_process_disassoc(dev, buf, len);
163 }
164} 118}
165EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt); 119EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
166 120
@@ -173,10 +127,7 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
173 trace_cfg80211_send_auth_timeout(dev, addr); 127 trace_cfg80211_send_auth_timeout(dev, addr);
174 128
175 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); 129 nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
176 if (wdev->sme_state == CFG80211_SME_CONNECTING) 130 cfg80211_sme_auth_timeout(wdev);
177 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
178 WLAN_STATUS_UNSPECIFIED_FAILURE,
179 false, NULL);
180} 131}
181EXPORT_SYMBOL(cfg80211_auth_timeout); 132EXPORT_SYMBOL(cfg80211_auth_timeout);
182 133
@@ -189,10 +140,7 @@ void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr)
189 trace_cfg80211_send_assoc_timeout(dev, addr); 140 trace_cfg80211_send_assoc_timeout(dev, addr);
190 141
191 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); 142 nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
192 if (wdev->sme_state == CFG80211_SME_CONNECTING) 143 cfg80211_sme_assoc_timeout(wdev);
193 __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
194 WLAN_STATUS_UNSPECIFIED_FAILURE,
195 false, NULL);
196} 144}
197EXPORT_SYMBOL(cfg80211_assoc_timeout); 145EXPORT_SYMBOL(cfg80211_assoc_timeout);
198 146
@@ -209,9 +157,9 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
209 return; 157 return;
210 158
211 if (ieee80211_is_deauth(mgmt->frame_control)) 159 if (ieee80211_is_deauth(mgmt->frame_control))
212 cfg80211_process_deauth(dev, buf, len); 160 cfg80211_process_deauth(wdev, buf, len);
213 else 161 else
214 cfg80211_process_disassoc(dev, buf, len); 162 cfg80211_process_disassoc(wdev, buf, len);
215} 163}
216EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt); 164EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
217 165
@@ -336,21 +284,12 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
336{ 284{
337 struct wireless_dev *wdev = dev->ieee80211_ptr; 285 struct wireless_dev *wdev = dev->ieee80211_ptr;
338 int err; 286 int err;
339 bool was_connected = false;
340 287
341 ASSERT_WDEV_LOCK(wdev); 288 ASSERT_WDEV_LOCK(wdev);
342 289
343 if (wdev->current_bss && req->prev_bssid && 290 if (wdev->current_bss &&
344 ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) { 291 (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
345 /* 292 req->prev_bssid)))
346 * Trying to reassociate: Allow this to proceed and let the old
347 * association to be dropped when the new one is completed.
348 */
349 if (wdev->sme_state == CFG80211_SME_CONNECTED) {
350 was_connected = true;
351 wdev->sme_state = CFG80211_SME_CONNECTING;
352 }
353 } else if (wdev->current_bss)
354 return -EALREADY; 293 return -EALREADY;
355 294
356 cfg80211_oper_and_ht_capa(&req->ht_capa_mask, 295 cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
@@ -360,11 +299,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
360 299
361 req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, 300 req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
362 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); 301 WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
363 if (!req->bss) { 302 if (!req->bss)
364 if (was_connected)
365 wdev->sme_state = CFG80211_SME_CONNECTED;
366 return -ENOENT; 303 return -ENOENT;
367 }
368 304
369 err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED); 305 err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
370 if (err) 306 if (err)
@@ -373,11 +309,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
373 err = rdev_assoc(rdev, dev, req); 309 err = rdev_assoc(rdev, dev, req);
374 310
375out: 311out:
376 if (err) { 312 if (err)
377 if (was_connected)
378 wdev->sme_state = CFG80211_SME_CONNECTED;
379 cfg80211_put_bss(&rdev->wiphy, req->bss); 313 cfg80211_put_bss(&rdev->wiphy, req->bss);
380 }
381 314
382 return err; 315 return err;
383} 316}
@@ -398,8 +331,9 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
398 331
399 ASSERT_WDEV_LOCK(wdev); 332 ASSERT_WDEV_LOCK(wdev);
400 333
401 if (local_state_change && (!wdev->current_bss || 334 if (local_state_change &&
402 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) 335 (!wdev->current_bss ||
336 !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
403 return 0; 337 return 0;
404 338
405 return rdev_deauth(rdev, dev, &req); 339 return rdev_deauth(rdev, dev, &req);
@@ -417,13 +351,11 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
417 .ie = ie, 351 .ie = ie,
418 .ie_len = ie_len, 352 .ie_len = ie_len,
419 }; 353 };
354 int err;
420 355
421 ASSERT_WDEV_LOCK(wdev); 356 ASSERT_WDEV_LOCK(wdev);
422 357
423 if (wdev->sme_state != CFG80211_SME_CONNECTED) 358 if (!wdev->current_bss)
424 return -ENOTCONN;
425
426 if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
427 return -ENOTCONN; 359 return -ENOTCONN;
428 360
429 if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) 361 if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
@@ -431,7 +363,13 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
431 else 363 else
432 return -ENOTCONN; 364 return -ENOTCONN;
433 365
434 return rdev_disassoc(rdev, dev, &req); 366 err = rdev_disassoc(rdev, dev, &req);
367 if (err)
368 return err;
369
370 /* driver should have reported the disassoc */
371 WARN_ON(wdev->current_bss);
372 return 0;
435} 373}
436 374
437void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, 375void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
@@ -439,10 +377,6 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
439{ 377{
440 struct wireless_dev *wdev = dev->ieee80211_ptr; 378 struct wireless_dev *wdev = dev->ieee80211_ptr;
441 u8 bssid[ETH_ALEN]; 379 u8 bssid[ETH_ALEN];
442 struct cfg80211_deauth_request req = {
443 .reason_code = WLAN_REASON_DEAUTH_LEAVING,
444 .bssid = bssid,
445 };
446 380
447 ASSERT_WDEV_LOCK(wdev); 381 ASSERT_WDEV_LOCK(wdev);
448 382
@@ -453,13 +387,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
453 return; 387 return;
454 388
455 memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); 389 memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
456 rdev_deauth(rdev, dev, &req); 390 cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
457 391 WLAN_REASON_DEAUTH_LEAVING, false);
458 if (wdev->current_bss) {
459 cfg80211_unhold_bss(wdev->current_bss);
460 cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
461 wdev->current_bss = NULL;
462 }
463} 392}
464 393
465struct cfg80211_mgmt_registration { 394struct cfg80211_mgmt_registration {