Blame view

net/wireless/wext-sme.c 9.17 KB
f21293549   Johannes Berg   cfg80211: managed...
1
2
3
4
5
6
  /*
   * cfg80211 wext compat for managed mode.
   *
   * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
   * Copyright (C) 2009   Intel Corporation. All rights reserved.
   */
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
7
  #include <linux/export.h>
f21293549   Johannes Berg   cfg80211: managed...
8
9
  #include <linux/etherdevice.h>
  #include <linux/if_arp.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
10
  #include <linux/slab.h>
f21293549   Johannes Berg   cfg80211: managed...
11
  #include <net/cfg80211.h>
262eb9b22   Johannes Berg   cfg80211: split w...
12
  #include <net/cfg80211-wext.h>
0e82ffe3b   Johannes Berg   cfg80211: combine...
13
  #include "wext-compat.h"
f21293549   Johannes Berg   cfg80211: managed...
14
  #include "nl80211.h"
fffd0934b   Johannes Berg   cfg80211: rework ...
15
16
  int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
  			      struct wireless_dev *wdev)
f21293549   Johannes Berg   cfg80211: managed...
17
  {
fffd0934b   Johannes Berg   cfg80211: rework ...
18
  	struct cfg80211_cached_keys *ck = NULL;
f401a6f7e   Johannes Berg   cfg80211: use rea...
19
  	const u8 *prev_bssid = NULL;
fffd0934b   Johannes Berg   cfg80211: rework ...
20
  	int err, i;
f21293549   Johannes Berg   cfg80211: managed...
21

667503ddc   Johannes Berg   cfg80211: fix loc...
22
23
  	ASSERT_RDEV_LOCK(rdev);
  	ASSERT_WDEV_LOCK(wdev);
f21293549   Johannes Berg   cfg80211: managed...
24
25
26
27
28
  	if (!netif_running(wdev->netdev))
  		return 0;
  
  	wdev->wext.connect.ie = wdev->wext.ie;
  	wdev->wext.connect.ie_len = wdev->wext.ie_len;
f21293549   Johannes Berg   cfg80211: managed...
29

fffd0934b   Johannes Berg   cfg80211: rework ...
30
31
32
  	if (wdev->wext.keys) {
  		wdev->wext.keys->def = wdev->wext.default_key;
  		wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
4be3bd8cc   Johannes Berg   cfg80211: don't s...
33
34
  		if (wdev->wext.default_key != -1)
  			wdev->wext.connect.privacy = true;
fffd0934b   Johannes Berg   cfg80211: rework ...
35
36
37
38
39
40
41
42
43
44
45
46
  	}
  
  	if (!wdev->wext.connect.ssid_len)
  		return 0;
  
  	if (wdev->wext.keys) {
  		ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
  		if (!ck)
  			return -ENOMEM;
  		for (i = 0; i < 6; i++)
  			ck->params[i].key = ck->data[i];
  	}
f401a6f7e   Johannes Berg   cfg80211: use rea...
47
48
49
  
  	if (wdev->wext.prev_bssid_valid)
  		prev_bssid = wdev->wext.prev_bssid;
fffd0934b   Johannes Berg   cfg80211: rework ...
50
  	err = __cfg80211_connect(rdev, wdev->netdev,
f401a6f7e   Johannes Berg   cfg80211: use rea...
51
  				 &wdev->wext.connect, ck, prev_bssid);
fffd0934b   Johannes Berg   cfg80211: rework ...
52
53
  	if (err)
  		kfree(ck);
f21293549   Johannes Berg   cfg80211: managed...
54
55
56
57
58
59
  
  	return err;
  }
  
  int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
  			      struct iw_request_info *info,
59bbb6f75   Johannes Berg   cfg80211: validat...
60
  			      struct iw_freq *wextfreq, char *extra)
f21293549   Johannes Berg   cfg80211: managed...
61
62
63
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
59bbb6f75   Johannes Berg   cfg80211: validat...
64
65
  	struct ieee80211_channel *chan = NULL;
  	int err, freq;
f21293549   Johannes Berg   cfg80211: managed...
66
67
68
69
  
  	/* call only for station! */
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return -EINVAL;
59bbb6f75   Johannes Berg   cfg80211: validat...
70
71
72
  	freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
  	if (freq < 0)
  		return freq;
f21293549   Johannes Berg   cfg80211: managed...
73

59bbb6f75   Johannes Berg   cfg80211: validat...
74
75
76
77
78
79
80
  	if (freq) {
  		chan = ieee80211_get_channel(wdev->wiphy, freq);
  		if (!chan)
  			return -EINVAL;
  		if (chan->flags & IEEE80211_CHAN_DISABLED)
  			return -EINVAL;
  	}
f21293549   Johannes Berg   cfg80211: managed...
81

667503ddc   Johannes Berg   cfg80211: fix loc...
82
  	cfg80211_lock_rdev(rdev);
59bbb6f75   Johannes Berg   cfg80211: validat...
83
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
84
  	wdev_lock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
85
86
  	if (wdev->sme_state != CFG80211_SME_IDLE) {
  		bool event = true;
25e83c490   Johannes Berg   cfg80211: don't o...
87
88
89
90
91
  
  		if (wdev->wext.connect.channel == chan) {
  			err = 0;
  			goto out;
  		}
f21293549   Johannes Berg   cfg80211: managed...
92
93
94
  		/* if SSID set, we'll try right again, avoid event */
  		if (wdev->wext.connect.ssid_len)
  			event = false;
59bbb6f75   Johannes Berg   cfg80211: validat...
95
96
  		err = __cfg80211_disconnect(rdev, dev,
  					    WLAN_REASON_DEAUTH_LEAVING, event);
f21293549   Johannes Berg   cfg80211: managed...
97
  		if (err)
667503ddc   Johannes Berg   cfg80211: fix loc...
98
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
99
  	}
667503ddc   Johannes Berg   cfg80211: fix loc...
100

f21293549   Johannes Berg   cfg80211: managed...
101
102
103
  	wdev->wext.connect.channel = chan;
  
  	/* SSID is not set, we just want to switch channel */
59bbb6f75   Johannes Berg   cfg80211: validat...
104
  	if (chan && !wdev->wext.connect.ssid_len) {
f444de05d   Johannes Berg   cfg80211/mac80211...
105
  		err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
667503ddc   Johannes Berg   cfg80211: fix loc...
106
  		goto out;
f21293549   Johannes Berg   cfg80211: managed...
107
  	}
59bbb6f75   Johannes Berg   cfg80211: validat...
108
  	err = cfg80211_mgd_wext_connect(rdev, wdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
109
110
   out:
  	wdev_unlock(wdev);
59bbb6f75   Johannes Berg   cfg80211: validat...
111
  	mutex_unlock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
112
113
  	cfg80211_unlock_rdev(rdev);
  	return err;
f21293549   Johannes Berg   cfg80211: managed...
114
  }
f21293549   Johannes Berg   cfg80211: managed...
115
116
117
118
119
120
121
122
123
124
125
  
  int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
  			      struct iw_request_info *info,
  			      struct iw_freq *freq, char *extra)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct ieee80211_channel *chan = NULL;
  
  	/* call only for station! */
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return -EINVAL;
667503ddc   Johannes Berg   cfg80211: fix loc...
126
  	wdev_lock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
127
  	if (wdev->current_bss)
19957bb39   Johannes Berg   cfg80211: keep tr...
128
  		chan = wdev->current_bss->pub.channel;
f21293549   Johannes Berg   cfg80211: managed...
129
130
  	else if (wdev->wext.connect.channel)
  		chan = wdev->wext.connect.channel;
667503ddc   Johannes Berg   cfg80211: fix loc...
131
  	wdev_unlock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
132
133
134
135
136
137
138
139
140
141
  
  	if (chan) {
  		freq->m = chan->center_freq;
  		freq->e = 6;
  		return 0;
  	}
  
  	/* no channel if not joining */
  	return -EINVAL;
  }
f21293549   Johannes Berg   cfg80211: managed...
142
143
144
145
146
147
  
  int cfg80211_mgd_wext_siwessid(struct net_device *dev,
  			       struct iw_request_info *info,
  			       struct iw_point *data, char *ssid)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
59bbb6f75   Johannes Berg   cfg80211: validat...
148
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
f21293549   Johannes Berg   cfg80211: managed...
149
150
151
152
153
154
155
156
157
158
159
160
161
  	size_t len = data->length;
  	int err;
  
  	/* call only for station! */
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return -EINVAL;
  
  	if (!data->flags)
  		len = 0;
  
  	/* iwconfig uses nul termination in SSID.. */
  	if (len > 0 && ssid[len - 1] == '\0')
  		len--;
59bbb6f75   Johannes Berg   cfg80211: validat...
162
163
  	cfg80211_lock_rdev(rdev);
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
164
165
166
  	wdev_lock(wdev);
  
  	err = 0;
f21293549   Johannes Berg   cfg80211: managed...
167
168
  	if (wdev->sme_state != CFG80211_SME_IDLE) {
  		bool event = true;
25e83c490   Johannes Berg   cfg80211: don't o...
169
170
171
172
173
  
  		if (wdev->wext.connect.ssid && len &&
  		    len == wdev->wext.connect.ssid_len &&
  		    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
174
175
176
  		/* if SSID set now, we'll try to connect, avoid event */
  		if (len)
  			event = false;
59bbb6f75   Johannes Berg   cfg80211: validat...
177
178
  		err = __cfg80211_disconnect(rdev, dev,
  					    WLAN_REASON_DEAUTH_LEAVING, event);
f21293549   Johannes Berg   cfg80211: managed...
179
  		if (err)
667503ddc   Johannes Berg   cfg80211: fix loc...
180
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
181
  	}
f401a6f7e   Johannes Berg   cfg80211: use rea...
182
  	wdev->wext.prev_bssid_valid = false;
f21293549   Johannes Berg   cfg80211: managed...
183
184
185
186
187
  	wdev->wext.connect.ssid = wdev->wext.ssid;
  	memcpy(wdev->wext.ssid, ssid, len);
  	wdev->wext.connect.ssid_len = len;
  
  	wdev->wext.connect.crypto.control_port = false;
c0692b8fe   Johannes Berg   cfg80211: allow c...
188
189
  	wdev->wext.connect.crypto.control_port_ethertype =
  					cpu_to_be16(ETH_P_PAE);
f21293549   Johannes Berg   cfg80211: managed...
190

59bbb6f75   Johannes Berg   cfg80211: validat...
191
  	err = cfg80211_mgd_wext_connect(rdev, wdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
192
193
   out:
  	wdev_unlock(wdev);
59bbb6f75   Johannes Berg   cfg80211: validat...
194
195
  	mutex_unlock(&rdev->devlist_mtx);
  	cfg80211_unlock_rdev(rdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
196
  	return err;
f21293549   Johannes Berg   cfg80211: managed...
197
  }
f21293549   Johannes Berg   cfg80211: managed...
198
199
200
201
202
203
204
205
206
207
208
209
  
  int cfg80211_mgd_wext_giwessid(struct net_device *dev,
  			       struct iw_request_info *info,
  			       struct iw_point *data, char *ssid)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  
  	/* call only for station! */
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return -EINVAL;
  
  	data->flags = 0;
667503ddc   Johannes Berg   cfg80211: fix loc...
210
  	wdev_lock(wdev);
a42dd7efd   Zhu Yi   wireless: display...
211
212
213
214
215
216
217
218
219
  	if (wdev->current_bss) {
  		const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
  						    WLAN_EID_SSID);
  		if (ie) {
  			data->flags = 1;
  			data->length = ie[1];
  			memcpy(ssid, ie + 2, data->length);
  		}
  	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
f21293549   Johannes Berg   cfg80211: managed...
220
221
222
  		data->flags = 1;
  		data->length = wdev->wext.connect.ssid_len;
  		memcpy(ssid, wdev->wext.connect.ssid, data->length);
33de4f9d7   Johannes Berg   cfg80211: wext: d...
223
  	}
667503ddc   Johannes Berg   cfg80211: fix loc...
224
  	wdev_unlock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
225
226
227
  
  	return 0;
  }
f21293549   Johannes Berg   cfg80211: managed...
228
229
230
231
232
233
  
  int cfg80211_mgd_wext_siwap(struct net_device *dev,
  			    struct iw_request_info *info,
  			    struct sockaddr *ap_addr, char *extra)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
59bbb6f75   Johannes Berg   cfg80211: validat...
234
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
f21293549   Johannes Berg   cfg80211: managed...
235
236
237
238
239
240
241
242
243
244
245
246
247
  	u8 *bssid = ap_addr->sa_data;
  	int err;
  
  	/* call only for station! */
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return -EINVAL;
  
  	if (ap_addr->sa_family != ARPHRD_ETHER)
  		return -EINVAL;
  
  	/* automatic mode */
  	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
  		bssid = NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
248
249
  	cfg80211_lock_rdev(rdev);
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
250
  	wdev_lock(wdev);
25e83c490   Johannes Berg   cfg80211: don't o...
251
252
253
254
255
  	if (wdev->sme_state != CFG80211_SME_IDLE) {
  		err = 0;
  		/* both automatic */
  		if (!bssid && !wdev->wext.connect.bssid)
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
256

25e83c490   Johannes Berg   cfg80211: don't o...
257
258
259
260
  		/* fixed already - and no change */
  		if (wdev->wext.connect.bssid && bssid &&
  		    compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0)
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
261

59bbb6f75   Johannes Berg   cfg80211: validat...
262
263
  		err = __cfg80211_disconnect(rdev, dev,
  					    WLAN_REASON_DEAUTH_LEAVING, false);
f21293549   Johannes Berg   cfg80211: managed...
264
  		if (err)
667503ddc   Johannes Berg   cfg80211: fix loc...
265
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
266
267
268
269
270
271
272
  	}
  
  	if (bssid) {
  		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
  		wdev->wext.connect.bssid = wdev->wext.bssid;
  	} else
  		wdev->wext.connect.bssid = NULL;
59bbb6f75   Johannes Berg   cfg80211: validat...
273
  	err = cfg80211_mgd_wext_connect(rdev, wdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
274
275
   out:
  	wdev_unlock(wdev);
59bbb6f75   Johannes Berg   cfg80211: validat...
276
277
  	mutex_unlock(&rdev->devlist_mtx);
  	cfg80211_unlock_rdev(rdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
278
  	return err;
f21293549   Johannes Berg   cfg80211: managed...
279
  }
f21293549   Johannes Berg   cfg80211: managed...
280
281
282
283
284
285
286
287
288
289
290
291
  
  int cfg80211_mgd_wext_giwap(struct net_device *dev,
  			    struct iw_request_info *info,
  			    struct sockaddr *ap_addr, char *extra)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  
  	/* call only for station! */
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return -EINVAL;
  
  	ap_addr->sa_family = ARPHRD_ETHER;
667503ddc   Johannes Berg   cfg80211: fix loc...
292
  	wdev_lock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
293
  	if (wdev->current_bss)
19957bb39   Johannes Berg   cfg80211: keep tr...
294
  		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
f21293549   Johannes Berg   cfg80211: managed...
295
296
  	else
  		memset(ap_addr->sa_data, 0, ETH_ALEN);
667503ddc   Johannes Berg   cfg80211: fix loc...
297
  	wdev_unlock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
298
299
300
  
  	return 0;
  }
f21293549   Johannes Berg   cfg80211: managed...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
  
  int cfg80211_wext_siwgenie(struct net_device *dev,
  			   struct iw_request_info *info,
  			   struct iw_point *data, char *extra)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
  	u8 *ie = extra;
  	int ie_len = data->length, err;
  
  	if (wdev->iftype != NL80211_IFTYPE_STATION)
  		return -EOPNOTSUPP;
  
  	if (!ie_len)
  		ie = NULL;
667503ddc   Johannes Berg   cfg80211: fix loc...
316
  	wdev_lock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
317
  	/* no change */
667503ddc   Johannes Berg   cfg80211: fix loc...
318
  	err = 0;
f21293549   Johannes Berg   cfg80211: managed...
319
320
  	if (wdev->wext.ie_len == ie_len &&
  	    memcmp(wdev->wext.ie, ie, ie_len) == 0)
667503ddc   Johannes Berg   cfg80211: fix loc...
321
  		goto out;
f21293549   Johannes Berg   cfg80211: managed...
322
323
324
  
  	if (ie_len) {
  		ie = kmemdup(extra, ie_len, GFP_KERNEL);
667503ddc   Johannes Berg   cfg80211: fix loc...
325
326
327
328
  		if (!ie) {
  			err = -ENOMEM;
  			goto out;
  		}
f21293549   Johannes Berg   cfg80211: managed...
329
330
331
332
333
334
335
336
  	} else
  		ie = NULL;
  
  	kfree(wdev->wext.ie);
  	wdev->wext.ie = ie;
  	wdev->wext.ie_len = ie_len;
  
  	if (wdev->sme_state != CFG80211_SME_IDLE) {
667503ddc   Johannes Berg   cfg80211: fix loc...
337
338
  		err = __cfg80211_disconnect(rdev, dev,
  					    WLAN_REASON_DEAUTH_LEAVING, false);
f21293549   Johannes Berg   cfg80211: managed...
339
  		if (err)
667503ddc   Johannes Berg   cfg80211: fix loc...
340
  			goto out;
f21293549   Johannes Berg   cfg80211: managed...
341
342
343
  	}
  
  	/* userspace better not think we'll reconnect */
667503ddc   Johannes Berg   cfg80211: fix loc...
344
345
346
347
  	err = 0;
   out:
  	wdev_unlock(wdev);
  	return err;
f21293549   Johannes Berg   cfg80211: managed...
348
  }
f21293549   Johannes Berg   cfg80211: managed...
349
350
351
352
353
354
355
356
  
  int cfg80211_wext_siwmlme(struct net_device *dev,
  			  struct iw_request_info *info,
  			  struct iw_point *data, char *extra)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct iw_mlme *mlme = (struct iw_mlme *)extra;
  	struct cfg80211_registered_device *rdev;
667503ddc   Johannes Berg   cfg80211: fix loc...
357
  	int err;
f21293549   Johannes Berg   cfg80211: managed...
358
359
360
361
362
363
364
365
366
367
368
  
  	if (!wdev)
  		return -EOPNOTSUPP;
  
  	rdev = wiphy_to_dev(wdev->wiphy);
  
  	if (wdev->iftype != NL80211_IFTYPE_STATION)
  		return -EINVAL;
  
  	if (mlme->addr.sa_family != ARPHRD_ETHER)
  		return -EINVAL;
667503ddc   Johannes Berg   cfg80211: fix loc...
369
  	wdev_lock(wdev);
f21293549   Johannes Berg   cfg80211: managed...
370
371
372
  	switch (mlme->cmd) {
  	case IW_MLME_DEAUTH:
  	case IW_MLME_DISASSOC:
667503ddc   Johannes Berg   cfg80211: fix loc...
373
374
375
  		err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
  					    true);
  		break;
f21293549   Johannes Berg   cfg80211: managed...
376
  	default:
667503ddc   Johannes Berg   cfg80211: fix loc...
377
378
  		err = -EOPNOTSUPP;
  		break;
f21293549   Johannes Berg   cfg80211: managed...
379
  	}
667503ddc   Johannes Berg   cfg80211: fix loc...
380
381
382
  	wdev_unlock(wdev);
  
  	return err;
f21293549   Johannes Berg   cfg80211: managed...
383
  }