Linux Audio

Check our new training course

Loading...
v6.8
  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, 2020-2023 Intel Corporation
  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	lockdep_assert_wiphy(wdev->wiphy);
 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 < 4; 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_sensitive(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	if (wdev->conn) {
 91		bool event = true;
 92
 93		if (wdev->wext.connect.channel == chan)
 94			return 0;
 
 
 95
 96		/* if SSID set, we'll try right again, avoid event */
 97		if (wdev->wext.connect.ssid_len)
 98			event = false;
 99		err = cfg80211_disconnect(rdev, dev,
100					  WLAN_REASON_DEAUTH_LEAVING, event);
101		if (err)
102			return err;
103	}
104
105	wdev->wext.connect.channel = chan;
106	return cfg80211_mgd_wext_connect(rdev, wdev);
 
 
 
107}
108
109int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
110			      struct iw_request_info *info,
111			      struct iw_freq *freq, char *extra)
112{
113	struct wireless_dev *wdev = dev->ieee80211_ptr;
114	struct ieee80211_channel *chan = NULL;
115
116	/* call only for station! */
117	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
118		return -EINVAL;
119
120	if (wdev->valid_links)
121		return -EOPNOTSUPP;
122
123	if (wdev->links[0].client.current_bss)
124		chan = wdev->links[0].client.current_bss->pub.channel;
125	else if (wdev->wext.connect.channel)
126		chan = wdev->wext.connect.channel;
 
127
128	if (chan) {
129		freq->m = chan->center_freq;
130		freq->e = 6;
131		return 0;
132	}
133
134	/* no channel if not joining */
135	return -EINVAL;
136}
137
138int cfg80211_mgd_wext_siwessid(struct net_device *dev,
139			       struct iw_request_info *info,
140			       struct iw_point *data, char *ssid)
141{
142	struct wireless_dev *wdev = dev->ieee80211_ptr;
143	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
144	size_t len = data->length;
145	int err;
146
147	/* call only for station! */
148	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
149		return -EINVAL;
150
151	if (!data->flags)
152		len = 0;
153
154	/* iwconfig uses nul termination in SSID.. */
155	if (len > 0 && ssid[len - 1] == '\0')
156		len--;
157
 
 
 
 
158	if (wdev->conn) {
159		bool event = true;
160
161		if (wdev->wext.connect.ssid && len &&
162		    len == wdev->wext.connect.ssid_len &&
163		    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
164			return 0;
165
166		/* if SSID set now, we'll try to connect, avoid event */
167		if (len)
168			event = false;
169		err = cfg80211_disconnect(rdev, dev,
170					  WLAN_REASON_DEAUTH_LEAVING, event);
171		if (err)
172			return err;
173	}
174
175	wdev->wext.prev_bssid_valid = false;
176	wdev->wext.connect.ssid = wdev->wext.ssid;
177	memcpy(wdev->wext.ssid, ssid, len);
178	wdev->wext.connect.ssid_len = len;
179
180	wdev->wext.connect.crypto.control_port = false;
181	wdev->wext.connect.crypto.control_port_ethertype =
182					cpu_to_be16(ETH_P_PAE);
183
184	return cfg80211_mgd_wext_connect(rdev, wdev);
 
 
 
185}
186
187int cfg80211_mgd_wext_giwessid(struct net_device *dev,
188			       struct iw_request_info *info,
189			       struct iw_point *data, char *ssid)
190{
191	struct wireless_dev *wdev = dev->ieee80211_ptr;
192	int ret = 0;
193
194	/* call only for station! */
195	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
196		return -EINVAL;
197
198	if (wdev->valid_links)
199		return -EINVAL;
200
201	data->flags = 0;
202
203	if (wdev->links[0].client.current_bss) {
204		const struct element *ssid_elem;
 
205
206		rcu_read_lock();
207		ssid_elem = ieee80211_bss_get_elem(
208				&wdev->links[0].client.current_bss->pub,
209				WLAN_EID_SSID);
210		if (ssid_elem) {
211			data->flags = 1;
212			data->length = ssid_elem->datalen;
213			if (data->length > IW_ESSID_MAX_SIZE)
214				ret = -EINVAL;
215			else
216				memcpy(ssid, ssid_elem->data, data->length);
217		}
218		rcu_read_unlock();
219	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
220		data->flags = 1;
221		data->length = wdev->wext.connect.ssid_len;
222		memcpy(ssid, wdev->wext.connect.ssid, data->length);
223	}
 
224
225	return ret;
226}
227
228int cfg80211_mgd_wext_siwap(struct net_device *dev,
229			    struct iw_request_info *info,
230			    struct sockaddr *ap_addr, char *extra)
231{
232	struct wireless_dev *wdev = dev->ieee80211_ptr;
233	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
234	u8 *bssid = ap_addr->sa_data;
235	int err;
236
237	/* call only for station! */
238	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
239		return -EINVAL;
240
241	if (ap_addr->sa_family != ARPHRD_ETHER)
242		return -EINVAL;
243
244	/* automatic mode */
245	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
246		bssid = NULL;
247
 
 
248	if (wdev->conn) {
 
249		/* both automatic */
250		if (!bssid && !wdev->wext.connect.bssid)
251			return 0;
252
253		/* fixed already - and no change */
254		if (wdev->wext.connect.bssid && bssid &&
255		    ether_addr_equal(bssid, wdev->wext.connect.bssid))
256			return 0;
257
258		err = cfg80211_disconnect(rdev, dev,
259					  WLAN_REASON_DEAUTH_LEAVING, false);
260		if (err)
261			return err;
262	}
263
264	if (bssid) {
265		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
266		wdev->wext.connect.bssid = wdev->wext.bssid;
267	} else
268		wdev->wext.connect.bssid = NULL;
269
270	return cfg80211_mgd_wext_connect(rdev, wdev);
 
 
 
271}
272
273int cfg80211_mgd_wext_giwap(struct net_device *dev,
274			    struct iw_request_info *info,
275			    struct sockaddr *ap_addr, char *extra)
276{
277	struct wireless_dev *wdev = dev->ieee80211_ptr;
278
279	/* call only for station! */
280	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
281		return -EINVAL;
282
283	ap_addr->sa_family = ARPHRD_ETHER;
284
285	if (wdev->valid_links)
286		return -EOPNOTSUPP;
287
288	if (wdev->links[0].client.current_bss)
289		memcpy(ap_addr->sa_data,
290		       wdev->links[0].client.current_bss->pub.bssid,
291		       ETH_ALEN);
292	else
293		eth_zero_addr(ap_addr->sa_data);
 
294
295	return 0;
296}
297
298int cfg80211_wext_siwgenie(struct net_device *dev,
299			   struct iw_request_info *info,
300			   union iwreq_data *wrqu, char *extra)
301{
302	struct iw_point *data = &wrqu->data;
303	struct wireless_dev *wdev = dev->ieee80211_ptr;
304	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
305	u8 *ie = extra;
306	int ie_len = data->length, err;
307
308	if (wdev->iftype != NL80211_IFTYPE_STATION)
309		return -EOPNOTSUPP;
310
311	if (!ie_len)
312		ie = NULL;
313
314	wiphy_lock(wdev->wiphy);
315
316	/* no change */
317	err = 0;
318	if (wdev->wext.ie_len == ie_len &&
319	    memcmp(wdev->wext.ie, ie, ie_len) == 0)
320		goto out;
321
322	if (ie_len) {
323		ie = kmemdup(extra, ie_len, GFP_KERNEL);
324		if (!ie) {
325			err = -ENOMEM;
326			goto out;
327		}
328	} else
329		ie = NULL;
330
331	kfree(wdev->wext.ie);
332	wdev->wext.ie = ie;
333	wdev->wext.ie_len = ie_len;
334
335	if (wdev->conn) {
336		err = cfg80211_disconnect(rdev, dev,
337					  WLAN_REASON_DEAUTH_LEAVING, false);
338		if (err)
339			goto out;
340	}
341
342	/* userspace better not think we'll reconnect */
343	err = 0;
344 out:
345	wiphy_unlock(wdev->wiphy);
346	return err;
347}
348
349int cfg80211_wext_siwmlme(struct net_device *dev,
350			  struct iw_request_info *info,
351			  union iwreq_data *wrqu, char *extra)
352{
353	struct wireless_dev *wdev = dev->ieee80211_ptr;
354	struct iw_mlme *mlme = (struct iw_mlme *)extra;
355	struct cfg80211_registered_device *rdev;
356	int err;
357
358	if (!wdev)
359		return -EOPNOTSUPP;
360
361	rdev = wiphy_to_rdev(wdev->wiphy);
362
363	if (wdev->iftype != NL80211_IFTYPE_STATION)
364		return -EINVAL;
365
366	if (mlme->addr.sa_family != ARPHRD_ETHER)
367		return -EINVAL;
368
369	wiphy_lock(&rdev->wiphy);
370	switch (mlme->cmd) {
371	case IW_MLME_DEAUTH:
372	case IW_MLME_DISASSOC:
373		err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
374		break;
375	default:
376		err = -EOPNOTSUPP;
377		break;
378	}
379	wiphy_unlock(&rdev->wiphy);
380
381	return err;
382}
v5.4
  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}