xref: /openbmc/linux/net/wireless/wext-sme.c (revision ee89bd6b)
1 /*
2  * cfg80211 wext compat for managed mode.
3  *
4  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
5  * Copyright (C) 2009   Intel Corporation. All rights reserved.
6  */
7 
8 #include <linux/export.h>
9 #include <linux/etherdevice.h>
10 #include <linux/if_arp.h>
11 #include <linux/slab.h>
12 #include <net/cfg80211.h>
13 #include <net/cfg80211-wext.h>
14 #include "wext-compat.h"
15 #include "nl80211.h"
16 
17 int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
18 			      struct wireless_dev *wdev)
19 {
20 	struct cfg80211_cached_keys *ck = NULL;
21 	const u8 *prev_bssid = NULL;
22 	int err, i;
23 
24 	ASSERT_RDEV_LOCK(rdev);
25 	ASSERT_WDEV_LOCK(wdev);
26 
27 	if (!netif_running(wdev->netdev))
28 		return 0;
29 
30 	wdev->wext.connect.ie = wdev->wext.ie;
31 	wdev->wext.connect.ie_len = wdev->wext.ie_len;
32 
33 	/* Use default background scan period */
34 	wdev->wext.connect.bg_scan_period = -1;
35 
36 	if (wdev->wext.keys) {
37 		wdev->wext.keys->def = wdev->wext.default_key;
38 		wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
39 		if (wdev->wext.default_key != -1)
40 			wdev->wext.connect.privacy = true;
41 	}
42 
43 	if (!wdev->wext.connect.ssid_len)
44 		return 0;
45 
46 	if (wdev->wext.keys) {
47 		ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
48 		if (!ck)
49 			return -ENOMEM;
50 		for (i = 0; i < 6; i++)
51 			ck->params[i].key = ck->data[i];
52 	}
53 
54 	if (wdev->wext.prev_bssid_valid)
55 		prev_bssid = wdev->wext.prev_bssid;
56 
57 	err = __cfg80211_connect(rdev, wdev->netdev,
58 				 &wdev->wext.connect, ck, prev_bssid);
59 	if (err)
60 		kfree(ck);
61 
62 	return err;
63 }
64 
65 int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
66 			      struct iw_request_info *info,
67 			      struct iw_freq *wextfreq, char *extra)
68 {
69 	struct wireless_dev *wdev = dev->ieee80211_ptr;
70 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
71 	struct ieee80211_channel *chan = NULL;
72 	int err, freq;
73 
74 	/* call only for station! */
75 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
76 		return -EINVAL;
77 
78 	freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
79 	if (freq < 0)
80 		return freq;
81 
82 	if (freq) {
83 		chan = ieee80211_get_channel(wdev->wiphy, freq);
84 		if (!chan)
85 			return -EINVAL;
86 		if (chan->flags & IEEE80211_CHAN_DISABLED)
87 			return -EINVAL;
88 	}
89 
90 	cfg80211_lock_rdev(rdev);
91 	mutex_lock(&rdev->devlist_mtx);
92 	mutex_lock(&rdev->sched_scan_mtx);
93 	wdev_lock(wdev);
94 
95 	if (wdev->sme_state != CFG80211_SME_IDLE) {
96 		bool event = true;
97 
98 		if (wdev->wext.connect.channel == chan) {
99 			err = 0;
100 			goto out;
101 		}
102 
103 		/* if SSID set, we'll try right again, avoid event */
104 		if (wdev->wext.connect.ssid_len)
105 			event = false;
106 		err = __cfg80211_disconnect(rdev, dev,
107 					    WLAN_REASON_DEAUTH_LEAVING, event);
108 		if (err)
109 			goto out;
110 	}
111 
112 
113 	wdev->wext.connect.channel = chan;
114 
115 	/*
116 	 * SSID is not set, we just want to switch monitor channel,
117 	 * this is really just backward compatibility, if the SSID
118 	 * is set then we use the channel to select the BSS to use
119 	 * to connect to instead. If we were connected on another
120 	 * channel we disconnected above and reconnect below.
121 	 */
122 	if (chan && !wdev->wext.connect.ssid_len) {
123 		struct cfg80211_chan_def chandef = {
124 			.width = NL80211_CHAN_WIDTH_20_NOHT,
125 			.center_freq1 = freq,
126 		};
127 
128 		chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
129 		if (chandef.chan)
130 			err = cfg80211_set_monitor_channel(rdev, &chandef);
131 		else
132 			err = -EINVAL;
133 		goto out;
134 	}
135 
136 	err = cfg80211_mgd_wext_connect(rdev, wdev);
137  out:
138 	wdev_unlock(wdev);
139 	mutex_unlock(&rdev->sched_scan_mtx);
140 	mutex_unlock(&rdev->devlist_mtx);
141 	cfg80211_unlock_rdev(rdev);
142 	return err;
143 }
144 
145 int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
146 			      struct iw_request_info *info,
147 			      struct iw_freq *freq, char *extra)
148 {
149 	struct wireless_dev *wdev = dev->ieee80211_ptr;
150 	struct ieee80211_channel *chan = NULL;
151 
152 	/* call only for station! */
153 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
154 		return -EINVAL;
155 
156 	wdev_lock(wdev);
157 	if (wdev->current_bss)
158 		chan = wdev->current_bss->pub.channel;
159 	else if (wdev->wext.connect.channel)
160 		chan = wdev->wext.connect.channel;
161 	wdev_unlock(wdev);
162 
163 	if (chan) {
164 		freq->m = chan->center_freq;
165 		freq->e = 6;
166 		return 0;
167 	}
168 
169 	/* no channel if not joining */
170 	return -EINVAL;
171 }
172 
173 int cfg80211_mgd_wext_siwessid(struct net_device *dev,
174 			       struct iw_request_info *info,
175 			       struct iw_point *data, char *ssid)
176 {
177 	struct wireless_dev *wdev = dev->ieee80211_ptr;
178 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
179 	size_t len = data->length;
180 	int err;
181 
182 	/* call only for station! */
183 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
184 		return -EINVAL;
185 
186 	if (!data->flags)
187 		len = 0;
188 
189 	/* iwconfig uses nul termination in SSID.. */
190 	if (len > 0 && ssid[len - 1] == '\0')
191 		len--;
192 
193 	cfg80211_lock_rdev(rdev);
194 	mutex_lock(&rdev->devlist_mtx);
195 	mutex_lock(&rdev->sched_scan_mtx);
196 	wdev_lock(wdev);
197 
198 	err = 0;
199 
200 	if (wdev->sme_state != CFG80211_SME_IDLE) {
201 		bool event = true;
202 
203 		if (wdev->wext.connect.ssid && len &&
204 		    len == wdev->wext.connect.ssid_len &&
205 		    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
206 			goto out;
207 
208 		/* if SSID set now, we'll try to connect, avoid event */
209 		if (len)
210 			event = false;
211 		err = __cfg80211_disconnect(rdev, dev,
212 					    WLAN_REASON_DEAUTH_LEAVING, event);
213 		if (err)
214 			goto out;
215 	}
216 
217 	wdev->wext.prev_bssid_valid = false;
218 	wdev->wext.connect.ssid = wdev->wext.ssid;
219 	memcpy(wdev->wext.ssid, ssid, len);
220 	wdev->wext.connect.ssid_len = len;
221 
222 	wdev->wext.connect.crypto.control_port = false;
223 	wdev->wext.connect.crypto.control_port_ethertype =
224 					cpu_to_be16(ETH_P_PAE);
225 
226 	err = cfg80211_mgd_wext_connect(rdev, wdev);
227  out:
228 	wdev_unlock(wdev);
229 	mutex_unlock(&rdev->sched_scan_mtx);
230 	mutex_unlock(&rdev->devlist_mtx);
231 	cfg80211_unlock_rdev(rdev);
232 	return err;
233 }
234 
235 int cfg80211_mgd_wext_giwessid(struct net_device *dev,
236 			       struct iw_request_info *info,
237 			       struct iw_point *data, char *ssid)
238 {
239 	struct wireless_dev *wdev = dev->ieee80211_ptr;
240 
241 	/* call only for station! */
242 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
243 		return -EINVAL;
244 
245 	data->flags = 0;
246 
247 	wdev_lock(wdev);
248 	if (wdev->current_bss) {
249 		const u8 *ie;
250 
251 		rcu_read_lock();
252 		ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
253 					  WLAN_EID_SSID);
254 		if (ie) {
255 			data->flags = 1;
256 			data->length = ie[1];
257 			memcpy(ssid, ie + 2, data->length);
258 		}
259 		rcu_read_unlock();
260 	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
261 		data->flags = 1;
262 		data->length = wdev->wext.connect.ssid_len;
263 		memcpy(ssid, wdev->wext.connect.ssid, data->length);
264 	}
265 	wdev_unlock(wdev);
266 
267 	return 0;
268 }
269 
270 int cfg80211_mgd_wext_siwap(struct net_device *dev,
271 			    struct iw_request_info *info,
272 			    struct sockaddr *ap_addr, char *extra)
273 {
274 	struct wireless_dev *wdev = dev->ieee80211_ptr;
275 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
276 	u8 *bssid = ap_addr->sa_data;
277 	int err;
278 
279 	/* call only for station! */
280 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
281 		return -EINVAL;
282 
283 	if (ap_addr->sa_family != ARPHRD_ETHER)
284 		return -EINVAL;
285 
286 	/* automatic mode */
287 	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
288 		bssid = NULL;
289 
290 	cfg80211_lock_rdev(rdev);
291 	mutex_lock(&rdev->devlist_mtx);
292 	mutex_lock(&rdev->sched_scan_mtx);
293 	wdev_lock(wdev);
294 
295 	if (wdev->sme_state != CFG80211_SME_IDLE) {
296 		err = 0;
297 		/* both automatic */
298 		if (!bssid && !wdev->wext.connect.bssid)
299 			goto out;
300 
301 		/* fixed already - and no change */
302 		if (wdev->wext.connect.bssid && bssid &&
303 		    ether_addr_equal(bssid, wdev->wext.connect.bssid))
304 			goto out;
305 
306 		err = __cfg80211_disconnect(rdev, dev,
307 					    WLAN_REASON_DEAUTH_LEAVING, false);
308 		if (err)
309 			goto out;
310 	}
311 
312 	if (bssid) {
313 		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
314 		wdev->wext.connect.bssid = wdev->wext.bssid;
315 	} else
316 		wdev->wext.connect.bssid = NULL;
317 
318 	err = cfg80211_mgd_wext_connect(rdev, wdev);
319  out:
320 	wdev_unlock(wdev);
321 	mutex_unlock(&rdev->sched_scan_mtx);
322 	mutex_unlock(&rdev->devlist_mtx);
323 	cfg80211_unlock_rdev(rdev);
324 	return err;
325 }
326 
327 int cfg80211_mgd_wext_giwap(struct net_device *dev,
328 			    struct iw_request_info *info,
329 			    struct sockaddr *ap_addr, char *extra)
330 {
331 	struct wireless_dev *wdev = dev->ieee80211_ptr;
332 
333 	/* call only for station! */
334 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
335 		return -EINVAL;
336 
337 	ap_addr->sa_family = ARPHRD_ETHER;
338 
339 	wdev_lock(wdev);
340 	if (wdev->current_bss)
341 		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
342 	else
343 		memset(ap_addr->sa_data, 0, ETH_ALEN);
344 	wdev_unlock(wdev);
345 
346 	return 0;
347 }
348 
349 int cfg80211_wext_siwgenie(struct net_device *dev,
350 			   struct iw_request_info *info,
351 			   struct iw_point *data, char *extra)
352 {
353 	struct wireless_dev *wdev = dev->ieee80211_ptr;
354 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
355 	u8 *ie = extra;
356 	int ie_len = data->length, err;
357 
358 	if (wdev->iftype != NL80211_IFTYPE_STATION)
359 		return -EOPNOTSUPP;
360 
361 	if (!ie_len)
362 		ie = NULL;
363 
364 	wdev_lock(wdev);
365 
366 	/* no change */
367 	err = 0;
368 	if (wdev->wext.ie_len == ie_len &&
369 	    memcmp(wdev->wext.ie, ie, ie_len) == 0)
370 		goto out;
371 
372 	if (ie_len) {
373 		ie = kmemdup(extra, ie_len, GFP_KERNEL);
374 		if (!ie) {
375 			err = -ENOMEM;
376 			goto out;
377 		}
378 	} else
379 		ie = NULL;
380 
381 	kfree(wdev->wext.ie);
382 	wdev->wext.ie = ie;
383 	wdev->wext.ie_len = ie_len;
384 
385 	if (wdev->sme_state != CFG80211_SME_IDLE) {
386 		err = __cfg80211_disconnect(rdev, dev,
387 					    WLAN_REASON_DEAUTH_LEAVING, false);
388 		if (err)
389 			goto out;
390 	}
391 
392 	/* userspace better not think we'll reconnect */
393 	err = 0;
394  out:
395 	wdev_unlock(wdev);
396 	return err;
397 }
398 
399 int cfg80211_wext_siwmlme(struct net_device *dev,
400 			  struct iw_request_info *info,
401 			  struct iw_point *data, char *extra)
402 {
403 	struct wireless_dev *wdev = dev->ieee80211_ptr;
404 	struct iw_mlme *mlme = (struct iw_mlme *)extra;
405 	struct cfg80211_registered_device *rdev;
406 	int err;
407 
408 	if (!wdev)
409 		return -EOPNOTSUPP;
410 
411 	rdev = wiphy_to_dev(wdev->wiphy);
412 
413 	if (wdev->iftype != NL80211_IFTYPE_STATION)
414 		return -EINVAL;
415 
416 	if (mlme->addr.sa_family != ARPHRD_ETHER)
417 		return -EINVAL;
418 
419 	wdev_lock(wdev);
420 	switch (mlme->cmd) {
421 	case IW_MLME_DEAUTH:
422 	case IW_MLME_DISASSOC:
423 		err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
424 					    true);
425 		break;
426 	default:
427 		err = -EOPNOTSUPP;
428 		break;
429 	}
430 	wdev_unlock(wdev);
431 
432 	return err;
433 }
434