18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * cfg80211 wext compat for managed mode.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
68c2ecf20Sopenharmony_ci * Copyright (C) 2009   Intel Corporation. All rights reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
118c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
148c2ecf20Sopenharmony_ci#include <net/cfg80211-wext.h>
158c2ecf20Sopenharmony_ci#include "wext-compat.h"
168c2ecf20Sopenharmony_ci#include "nl80211.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
198c2ecf20Sopenharmony_ci			      struct wireless_dev *wdev)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct cfg80211_cached_keys *ck = NULL;
228c2ecf20Sopenharmony_ci	const u8 *prev_bssid = NULL;
238c2ecf20Sopenharmony_ci	int err, i;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	ASSERT_RTNL();
268c2ecf20Sopenharmony_ci	ASSERT_WDEV_LOCK(wdev);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (!netif_running(wdev->netdev))
298c2ecf20Sopenharmony_ci		return 0;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	wdev->wext.connect.ie = wdev->wext.ie;
328c2ecf20Sopenharmony_ci	wdev->wext.connect.ie_len = wdev->wext.ie_len;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* Use default background scan period */
358c2ecf20Sopenharmony_ci	wdev->wext.connect.bg_scan_period = -1;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (wdev->wext.keys) {
388c2ecf20Sopenharmony_ci		wdev->wext.keys->def = wdev->wext.default_key;
398c2ecf20Sopenharmony_ci		if (wdev->wext.default_key != -1)
408c2ecf20Sopenharmony_ci			wdev->wext.connect.privacy = true;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (!wdev->wext.connect.ssid_len)
448c2ecf20Sopenharmony_ci		return 0;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (wdev->wext.keys && wdev->wext.keys->def != -1) {
478c2ecf20Sopenharmony_ci		ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
488c2ecf20Sopenharmony_ci		if (!ck)
498c2ecf20Sopenharmony_ci			return -ENOMEM;
508c2ecf20Sopenharmony_ci		for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
518c2ecf20Sopenharmony_ci			ck->params[i].key = ck->data[i];
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (wdev->wext.prev_bssid_valid)
558c2ecf20Sopenharmony_ci		prev_bssid = wdev->wext.prev_bssid;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	err = cfg80211_connect(rdev, wdev->netdev,
588c2ecf20Sopenharmony_ci			       &wdev->wext.connect, ck, prev_bssid);
598c2ecf20Sopenharmony_ci	if (err)
608c2ecf20Sopenharmony_ci		kfree_sensitive(ck);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return err;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_siwfreq(struct net_device *dev,
668c2ecf20Sopenharmony_ci			      struct iw_request_info *info,
678c2ecf20Sopenharmony_ci			      struct iw_freq *wextfreq, char *extra)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
708c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
718c2ecf20Sopenharmony_ci	struct ieee80211_channel *chan = NULL;
728c2ecf20Sopenharmony_ci	int err, freq;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* call only for station! */
758c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
768c2ecf20Sopenharmony_ci		return -EINVAL;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	freq = cfg80211_wext_freq(wextfreq);
798c2ecf20Sopenharmony_ci	if (freq < 0)
808c2ecf20Sopenharmony_ci		return freq;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (freq) {
838c2ecf20Sopenharmony_ci		chan = ieee80211_get_channel(wdev->wiphy, freq);
848c2ecf20Sopenharmony_ci		if (!chan)
858c2ecf20Sopenharmony_ci			return -EINVAL;
868c2ecf20Sopenharmony_ci		if (chan->flags & IEEE80211_CHAN_DISABLED)
878c2ecf20Sopenharmony_ci			return -EINVAL;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	wdev_lock(wdev);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (wdev->conn) {
938c2ecf20Sopenharmony_ci		bool event = true;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		if (wdev->wext.connect.channel == chan) {
968c2ecf20Sopenharmony_ci			err = 0;
978c2ecf20Sopenharmony_ci			goto out;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		/* if SSID set, we'll try right again, avoid event */
1018c2ecf20Sopenharmony_ci		if (wdev->wext.connect.ssid_len)
1028c2ecf20Sopenharmony_ci			event = false;
1038c2ecf20Sopenharmony_ci		err = cfg80211_disconnect(rdev, dev,
1048c2ecf20Sopenharmony_ci					  WLAN_REASON_DEAUTH_LEAVING, event);
1058c2ecf20Sopenharmony_ci		if (err)
1068c2ecf20Sopenharmony_ci			goto out;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	wdev->wext.connect.channel = chan;
1108c2ecf20Sopenharmony_ci	err = cfg80211_mgd_wext_connect(rdev, wdev);
1118c2ecf20Sopenharmony_ci out:
1128c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
1138c2ecf20Sopenharmony_ci	return err;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_giwfreq(struct net_device *dev,
1178c2ecf20Sopenharmony_ci			      struct iw_request_info *info,
1188c2ecf20Sopenharmony_ci			      struct iw_freq *freq, char *extra)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
1218c2ecf20Sopenharmony_ci	struct ieee80211_channel *chan = NULL;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* call only for station! */
1248c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
1258c2ecf20Sopenharmony_ci		return -EINVAL;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	wdev_lock(wdev);
1288c2ecf20Sopenharmony_ci	if (wdev->current_bss)
1298c2ecf20Sopenharmony_ci		chan = wdev->current_bss->pub.channel;
1308c2ecf20Sopenharmony_ci	else if (wdev->wext.connect.channel)
1318c2ecf20Sopenharmony_ci		chan = wdev->wext.connect.channel;
1328c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (chan) {
1358c2ecf20Sopenharmony_ci		freq->m = chan->center_freq;
1368c2ecf20Sopenharmony_ci		freq->e = 6;
1378c2ecf20Sopenharmony_ci		return 0;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* no channel if not joining */
1418c2ecf20Sopenharmony_ci	return -EINVAL;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_siwessid(struct net_device *dev,
1458c2ecf20Sopenharmony_ci			       struct iw_request_info *info,
1468c2ecf20Sopenharmony_ci			       struct iw_point *data, char *ssid)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
1498c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1508c2ecf20Sopenharmony_ci	size_t len = data->length;
1518c2ecf20Sopenharmony_ci	int err;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* call only for station! */
1548c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
1558c2ecf20Sopenharmony_ci		return -EINVAL;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (!data->flags)
1588c2ecf20Sopenharmony_ci		len = 0;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* iwconfig uses nul termination in SSID.. */
1618c2ecf20Sopenharmony_ci	if (len > 0 && ssid[len - 1] == '\0')
1628c2ecf20Sopenharmony_ci		len--;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	wdev_lock(wdev);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	err = 0;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (wdev->conn) {
1698c2ecf20Sopenharmony_ci		bool event = true;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (wdev->wext.connect.ssid && len &&
1728c2ecf20Sopenharmony_ci		    len == wdev->wext.connect.ssid_len &&
1738c2ecf20Sopenharmony_ci		    memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
1748c2ecf20Sopenharmony_ci			goto out;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		/* if SSID set now, we'll try to connect, avoid event */
1778c2ecf20Sopenharmony_ci		if (len)
1788c2ecf20Sopenharmony_ci			event = false;
1798c2ecf20Sopenharmony_ci		err = cfg80211_disconnect(rdev, dev,
1808c2ecf20Sopenharmony_ci					  WLAN_REASON_DEAUTH_LEAVING, event);
1818c2ecf20Sopenharmony_ci		if (err)
1828c2ecf20Sopenharmony_ci			goto out;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	wdev->wext.prev_bssid_valid = false;
1868c2ecf20Sopenharmony_ci	wdev->wext.connect.ssid = wdev->wext.ssid;
1878c2ecf20Sopenharmony_ci	memcpy(wdev->wext.ssid, ssid, len);
1888c2ecf20Sopenharmony_ci	wdev->wext.connect.ssid_len = len;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	wdev->wext.connect.crypto.control_port = false;
1918c2ecf20Sopenharmony_ci	wdev->wext.connect.crypto.control_port_ethertype =
1928c2ecf20Sopenharmony_ci					cpu_to_be16(ETH_P_PAE);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	err = cfg80211_mgd_wext_connect(rdev, wdev);
1958c2ecf20Sopenharmony_ci out:
1968c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
1978c2ecf20Sopenharmony_ci	return err;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_giwessid(struct net_device *dev,
2018c2ecf20Sopenharmony_ci			       struct iw_request_info *info,
2028c2ecf20Sopenharmony_ci			       struct iw_point *data, char *ssid)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
2058c2ecf20Sopenharmony_ci	int ret = 0;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* call only for station! */
2088c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
2098c2ecf20Sopenharmony_ci		return -EINVAL;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	data->flags = 0;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	wdev_lock(wdev);
2148c2ecf20Sopenharmony_ci	if (wdev->current_bss) {
2158c2ecf20Sopenharmony_ci		const u8 *ie;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		rcu_read_lock();
2188c2ecf20Sopenharmony_ci		ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
2198c2ecf20Sopenharmony_ci					  WLAN_EID_SSID);
2208c2ecf20Sopenharmony_ci		if (ie) {
2218c2ecf20Sopenharmony_ci			data->flags = 1;
2228c2ecf20Sopenharmony_ci			data->length = ie[1];
2238c2ecf20Sopenharmony_ci			if (data->length > IW_ESSID_MAX_SIZE)
2248c2ecf20Sopenharmony_ci				ret = -EINVAL;
2258c2ecf20Sopenharmony_ci			else
2268c2ecf20Sopenharmony_ci				memcpy(ssid, ie + 2, data->length);
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci		rcu_read_unlock();
2298c2ecf20Sopenharmony_ci	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
2308c2ecf20Sopenharmony_ci		data->flags = 1;
2318c2ecf20Sopenharmony_ci		data->length = wdev->wext.connect.ssid_len;
2328c2ecf20Sopenharmony_ci		memcpy(ssid, wdev->wext.connect.ssid, data->length);
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_siwap(struct net_device *dev,
2408c2ecf20Sopenharmony_ci			    struct iw_request_info *info,
2418c2ecf20Sopenharmony_ci			    struct sockaddr *ap_addr, char *extra)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
2448c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
2458c2ecf20Sopenharmony_ci	u8 *bssid = ap_addr->sa_data;
2468c2ecf20Sopenharmony_ci	int err;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* call only for station! */
2498c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
2508c2ecf20Sopenharmony_ci		return -EINVAL;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (ap_addr->sa_family != ARPHRD_ETHER)
2538c2ecf20Sopenharmony_ci		return -EINVAL;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* automatic mode */
2568c2ecf20Sopenharmony_ci	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
2578c2ecf20Sopenharmony_ci		bssid = NULL;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	wdev_lock(wdev);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (wdev->conn) {
2628c2ecf20Sopenharmony_ci		err = 0;
2638c2ecf20Sopenharmony_ci		/* both automatic */
2648c2ecf20Sopenharmony_ci		if (!bssid && !wdev->wext.connect.bssid)
2658c2ecf20Sopenharmony_ci			goto out;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		/* fixed already - and no change */
2688c2ecf20Sopenharmony_ci		if (wdev->wext.connect.bssid && bssid &&
2698c2ecf20Sopenharmony_ci		    ether_addr_equal(bssid, wdev->wext.connect.bssid))
2708c2ecf20Sopenharmony_ci			goto out;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		err = cfg80211_disconnect(rdev, dev,
2738c2ecf20Sopenharmony_ci					  WLAN_REASON_DEAUTH_LEAVING, false);
2748c2ecf20Sopenharmony_ci		if (err)
2758c2ecf20Sopenharmony_ci			goto out;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (bssid) {
2798c2ecf20Sopenharmony_ci		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
2808c2ecf20Sopenharmony_ci		wdev->wext.connect.bssid = wdev->wext.bssid;
2818c2ecf20Sopenharmony_ci	} else
2828c2ecf20Sopenharmony_ci		wdev->wext.connect.bssid = NULL;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	err = cfg80211_mgd_wext_connect(rdev, wdev);
2858c2ecf20Sopenharmony_ci out:
2868c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
2878c2ecf20Sopenharmony_ci	return err;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ciint cfg80211_mgd_wext_giwap(struct net_device *dev,
2918c2ecf20Sopenharmony_ci			    struct iw_request_info *info,
2928c2ecf20Sopenharmony_ci			    struct sockaddr *ap_addr, char *extra)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* call only for station! */
2978c2ecf20Sopenharmony_ci	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
2988c2ecf20Sopenharmony_ci		return -EINVAL;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ap_addr->sa_family = ARPHRD_ETHER;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	wdev_lock(wdev);
3038c2ecf20Sopenharmony_ci	if (wdev->current_bss)
3048c2ecf20Sopenharmony_ci		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
3058c2ecf20Sopenharmony_ci	else
3068c2ecf20Sopenharmony_ci		eth_zero_addr(ap_addr->sa_data);
3078c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ciint cfg80211_wext_siwgenie(struct net_device *dev,
3138c2ecf20Sopenharmony_ci			   struct iw_request_info *info,
3148c2ecf20Sopenharmony_ci			   struct iw_point *data, char *extra)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
3178c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
3188c2ecf20Sopenharmony_ci	u8 *ie = extra;
3198c2ecf20Sopenharmony_ci	int ie_len = data->length, err;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (wdev->iftype != NL80211_IFTYPE_STATION)
3228c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (!ie_len)
3258c2ecf20Sopenharmony_ci		ie = NULL;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	wdev_lock(wdev);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* no change */
3308c2ecf20Sopenharmony_ci	err = 0;
3318c2ecf20Sopenharmony_ci	if (wdev->wext.ie_len == ie_len &&
3328c2ecf20Sopenharmony_ci	    memcmp(wdev->wext.ie, ie, ie_len) == 0)
3338c2ecf20Sopenharmony_ci		goto out;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (ie_len) {
3368c2ecf20Sopenharmony_ci		ie = kmemdup(extra, ie_len, GFP_KERNEL);
3378c2ecf20Sopenharmony_ci		if (!ie) {
3388c2ecf20Sopenharmony_ci			err = -ENOMEM;
3398c2ecf20Sopenharmony_ci			goto out;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	} else
3428c2ecf20Sopenharmony_ci		ie = NULL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	kfree(wdev->wext.ie);
3458c2ecf20Sopenharmony_ci	wdev->wext.ie = ie;
3468c2ecf20Sopenharmony_ci	wdev->wext.ie_len = ie_len;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (wdev->conn) {
3498c2ecf20Sopenharmony_ci		err = cfg80211_disconnect(rdev, dev,
3508c2ecf20Sopenharmony_ci					  WLAN_REASON_DEAUTH_LEAVING, false);
3518c2ecf20Sopenharmony_ci		if (err)
3528c2ecf20Sopenharmony_ci			goto out;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* userspace better not think we'll reconnect */
3568c2ecf20Sopenharmony_ci	err = 0;
3578c2ecf20Sopenharmony_ci out:
3588c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
3598c2ecf20Sopenharmony_ci	return err;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ciint cfg80211_wext_siwmlme(struct net_device *dev,
3638c2ecf20Sopenharmony_ci			  struct iw_request_info *info,
3648c2ecf20Sopenharmony_ci			  struct iw_point *data, char *extra)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct wireless_dev *wdev = dev->ieee80211_ptr;
3678c2ecf20Sopenharmony_ci	struct iw_mlme *mlme = (struct iw_mlme *)extra;
3688c2ecf20Sopenharmony_ci	struct cfg80211_registered_device *rdev;
3698c2ecf20Sopenharmony_ci	int err;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (!wdev)
3728c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	rdev = wiphy_to_rdev(wdev->wiphy);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (wdev->iftype != NL80211_IFTYPE_STATION)
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (mlme->addr.sa_family != ARPHRD_ETHER)
3808c2ecf20Sopenharmony_ci		return -EINVAL;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	wdev_lock(wdev);
3838c2ecf20Sopenharmony_ci	switch (mlme->cmd) {
3848c2ecf20Sopenharmony_ci	case IW_MLME_DEAUTH:
3858c2ecf20Sopenharmony_ci	case IW_MLME_DISASSOC:
3868c2ecf20Sopenharmony_ci		err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
3878c2ecf20Sopenharmony_ci		break;
3888c2ecf20Sopenharmony_ci	default:
3898c2ecf20Sopenharmony_ci		err = -EOPNOTSUPP;
3908c2ecf20Sopenharmony_ci		break;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci	wdev_unlock(wdev);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return err;
3958c2ecf20Sopenharmony_ci}
396