PageRenderTime 62ms CodeModel.GetById 14ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 1ms

/net/wireless/wext-sme.c

http://github.com/mirrors/linux
C | 395 lines | 297 code | 75 blank | 23 comment | 82 complexity | e74d5a860d38b7aee9681ab99c94dd79 MD5 | raw file
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * cfg80211 wext compat for managed mode.
  4 *
  5 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
  6 * Copyright (C) 2009   Intel Corporation. All rights reserved.
  7 */
  8
  9#include <linux/export.h>
 10#include <linux/etherdevice.h>
 11#include <linux/if_arp.h>
 12#include <linux/slab.h>
 13#include <net/cfg80211.h>
 14#include <net/cfg80211-wext.h>
 15#include "wext-compat.h"
 16#include "nl80211.h"
 17
 18int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
 19			      struct wireless_dev *wdev)
 20{
 21	struct cfg80211_cached_keys *ck = NULL;
 22	const u8 *prev_bssid = NULL;
 23	int err, i;
 24
 25	ASSERT_RTNL();
 26	ASSERT_WDEV_LOCK(wdev);
 27
 28	if (!netif_running(wdev->netdev))
 29		return 0;
 30
 31	wdev->wext.connect.ie = wdev->wext.ie;
 32	wdev->wext.connect.ie_len = wdev->wext.ie_len;
 33
 34	/* Use default background scan period */
 35	wdev->wext.connect.bg_scan_period = -1;
 36
 37	if (wdev->wext.keys) {
 38		wdev->wext.keys->def = wdev->wext.default_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 && wdev->wext.keys->def != -1) {
 47		ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
 48		if (!ck)
 49			return -ENOMEM;
 50		for (i = 0; i < CFG80211_MAX_WEP_KEYS; 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		kzfree(ck);
 61
 62	return err;
 63}
 64
 65int 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_rdev(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(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	wdev_lock(wdev);
 91
 92	if (wdev->conn) {
 93		bool event = true;
 94
 95		if (wdev->wext.connect.channel == chan) {
 96			err = 0;
 97			goto out;
 98		}
 99
100		/* if SSID set, we'll try right again, avoid event */
101		if (wdev->wext.connect.ssid_len)
102			event = false;
103		err = cfg80211_disconnect(rdev, dev,
104					  WLAN_REASON_DEAUTH_LEAVING, event);
105		if (err)
106			goto out;
107	}
108
109	wdev->wext.connect.channel = chan;
110	err = cfg80211_mgd_wext_connect(rdev, wdev);
111 out:
112	wdev_unlock(wdev);
113	return err;
114}
115
116int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
117			      struct iw_request_info *info,
118			      struct iw_freq *freq, char *extra)
119{
120	struct wireless_dev *wdev = dev->ieee80211_ptr;
121	struct ieee80211_channel *chan = NULL;
122
123	/* call only for station! */
124	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
125		return -EINVAL;
126
127	wdev_lock(wdev);
128	if (wdev->current_bss)
129		chan = wdev->current_bss->pub.channel;
130	else if (wdev->wext.connect.channel)
131		chan = wdev->wext.connect.channel;
132	wdev_unlock(wdev);
133
134	if (chan) {
135		freq->m = chan->center_freq;
136		freq->e = 6;
137		return 0;
138	}
139
140	/* no channel if not joining */
141	return -EINVAL;
142}
143
144int cfg80211_mgd_wext_siwessid(struct net_device *dev,
145			       struct iw_request_info *info,
146			       struct iw_point *data, char *ssid)
147{
148	struct wireless_dev *wdev = dev->ieee80211_ptr;
149	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
150	size_t len = data->length;
151	int err;
152
153	/* call only for station! */
154	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
155		return -EINVAL;
156
157	if (!data->flags)
158		len = 0;
159
160	/* iwconfig uses nul termination in SSID.. */
161	if (len > 0 && ssid[len - 1] == '\0')
162		len--;
163
164	wdev_lock(wdev);
165
166	err = 0;
167
168	if (wdev->conn) {
169		bool event = true;
170
171		if (wdev->wext.connect.ssid && len &&
172		    len == wdev->wext.connect.ssid_len &&
173		    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
174			goto out;
175
176		/* if SSID set now, we'll try to connect, avoid event */
177		if (len)
178			event = false;
179		err = cfg80211_disconnect(rdev, dev,
180					  WLAN_REASON_DEAUTH_LEAVING, event);
181		if (err)
182			goto out;
183	}
184
185	wdev->wext.prev_bssid_valid = false;
186	wdev->wext.connect.ssid = wdev->wext.ssid;
187	memcpy(wdev->wext.ssid, ssid, len);
188	wdev->wext.connect.ssid_len = len;
189
190	wdev->wext.connect.crypto.control_port = false;
191	wdev->wext.connect.crypto.control_port_ethertype =
192					cpu_to_be16(ETH_P_PAE);
193
194	err = cfg80211_mgd_wext_connect(rdev, wdev);
195 out:
196	wdev_unlock(wdev);
197	return err;
198}
199
200int cfg80211_mgd_wext_giwessid(struct net_device *dev,
201			       struct iw_request_info *info,
202			       struct iw_point *data, char *ssid)
203{
204	struct wireless_dev *wdev = dev->ieee80211_ptr;
205	int ret = 0;
206
207	/* call only for station! */
208	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
209		return -EINVAL;
210
211	data->flags = 0;
212
213	wdev_lock(wdev);
214	if (wdev->current_bss) {
215		const u8 *ie;
216
217		rcu_read_lock();
218		ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
219					  WLAN_EID_SSID);
220		if (ie) {
221			data->flags = 1;
222			data->length = ie[1];
223			if (data->length > IW_ESSID_MAX_SIZE)
224				ret = -EINVAL;
225			else
226				memcpy(ssid, ie + 2, data->length);
227		}
228		rcu_read_unlock();
229	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
230		data->flags = 1;
231		data->length = wdev->wext.connect.ssid_len;
232		memcpy(ssid, wdev->wext.connect.ssid, data->length);
233	}
234	wdev_unlock(wdev);
235
236	return ret;
237}
238
239int cfg80211_mgd_wext_siwap(struct net_device *dev,
240			    struct iw_request_info *info,
241			    struct sockaddr *ap_addr, char *extra)
242{
243	struct wireless_dev *wdev = dev->ieee80211_ptr;
244	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
245	u8 *bssid = ap_addr->sa_data;
246	int err;
247
248	/* call only for station! */
249	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
250		return -EINVAL;
251
252	if (ap_addr->sa_family != ARPHRD_ETHER)
253		return -EINVAL;
254
255	/* automatic mode */
256	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
257		bssid = NULL;
258
259	wdev_lock(wdev);
260
261	if (wdev->conn) {
262		err = 0;
263		/* both automatic */
264		if (!bssid && !wdev->wext.connect.bssid)
265			goto out;
266
267		/* fixed already - and no change */
268		if (wdev->wext.connect.bssid && bssid &&
269		    ether_addr_equal(bssid, wdev->wext.connect.bssid))
270			goto out;
271
272		err = cfg80211_disconnect(rdev, dev,
273					  WLAN_REASON_DEAUTH_LEAVING, false);
274		if (err)
275			goto out;
276	}
277
278	if (bssid) {
279		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
280		wdev->wext.connect.bssid = wdev->wext.bssid;
281	} else
282		wdev->wext.connect.bssid = NULL;
283
284	err = cfg80211_mgd_wext_connect(rdev, wdev);
285 out:
286	wdev_unlock(wdev);
287	return err;
288}
289
290int cfg80211_mgd_wext_giwap(struct net_device *dev,
291			    struct iw_request_info *info,
292			    struct sockaddr *ap_addr, char *extra)
293{
294	struct wireless_dev *wdev = dev->ieee80211_ptr;
295
296	/* call only for station! */
297	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
298		return -EINVAL;
299
300	ap_addr->sa_family = ARPHRD_ETHER;
301
302	wdev_lock(wdev);
303	if (wdev->current_bss)
304		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
305	else
306		eth_zero_addr(ap_addr->sa_data);
307	wdev_unlock(wdev);
308
309	return 0;
310}
311
312int cfg80211_wext_siwgenie(struct net_device *dev,
313			   struct iw_request_info *info,
314			   struct iw_point *data, char *extra)
315{
316	struct wireless_dev *wdev = dev->ieee80211_ptr;
317	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
318	u8 *ie = extra;
319	int ie_len = data->length, err;
320
321	if (wdev->iftype != NL80211_IFTYPE_STATION)
322		return -EOPNOTSUPP;
323
324	if (!ie_len)
325		ie = NULL;
326
327	wdev_lock(wdev);
328
329	/* no change */
330	err = 0;
331	if (wdev->wext.ie_len == ie_len &&
332	    memcmp(wdev->wext.ie, ie, ie_len) == 0)
333		goto out;
334
335	if (ie_len) {
336		ie = kmemdup(extra, ie_len, GFP_KERNEL);
337		if (!ie) {
338			err = -ENOMEM;
339			goto out;
340		}
341	} else
342		ie = NULL;
343
344	kfree(wdev->wext.ie);
345	wdev->wext.ie = ie;
346	wdev->wext.ie_len = ie_len;
347
348	if (wdev->conn) {
349		err = cfg80211_disconnect(rdev, dev,
350					  WLAN_REASON_DEAUTH_LEAVING, false);
351		if (err)
352			goto out;
353	}
354
355	/* userspace better not think we'll reconnect */
356	err = 0;
357 out:
358	wdev_unlock(wdev);
359	return err;
360}
361
362int cfg80211_wext_siwmlme(struct net_device *dev,
363			  struct iw_request_info *info,
364			  struct iw_point *data, char *extra)
365{
366	struct wireless_dev *wdev = dev->ieee80211_ptr;
367	struct iw_mlme *mlme = (struct iw_mlme *)extra;
368	struct cfg80211_registered_device *rdev;
369	int err;
370
371	if (!wdev)
372		return -EOPNOTSUPP;
373
374	rdev = wiphy_to_rdev(wdev->wiphy);
375
376	if (wdev->iftype != NL80211_IFTYPE_STATION)
377		return -EINVAL;
378
379	if (mlme->addr.sa_family != ARPHRD_ETHER)
380		return -EINVAL;
381
382	wdev_lock(wdev);
383	switch (mlme->cmd) {
384	case IW_MLME_DEAUTH:
385	case IW_MLME_DISASSOC:
386		err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
387		break;
388	default:
389		err = -EOPNOTSUPP;
390		break;
391	}
392	wdev_unlock(wdev);
393
394	return err;
395}