diff options
Diffstat (limited to 'net/wireless/nl80211.c')
| -rw-r--r-- | net/wireless/nl80211.c | 154 | 
1 files changed, 150 insertions, 4 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e447db04cf7..356a84a5dae 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7,6 +7,7 @@  #include <linux/if.h>  #include <linux/module.h>  #include <linux/err.h> +#include <linux/slab.h>  #include <linux/list.h>  #include <linux/if_ether.h>  #include <linux/ieee80211.h> @@ -149,6 +150,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {  				 .len = IEEE80211_MAX_DATA_LEN },  	[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },  	[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, +	[NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, +	[NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },  };  /* policy for the attributes */ @@ -2095,7 +2098,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)  		goto out_rtnl;  	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && -	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { +	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && +	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {  		err = -EINVAL;  		goto out;  	} @@ -3391,6 +3395,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)  	int err, ssid_len, ie_len = 0;  	enum nl80211_auth_type auth_type;  	struct key_parse key; +	bool local_state_change;  	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))  		return -EINVAL; @@ -3469,9 +3474,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)  		goto out;  	} +	local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; +  	err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,  				 ssid, ssid_len, ie, ie_len, -				 key.p.key, key.p.key_len, key.idx); +				 key.p.key, key.p.key_len, key.idx, +				 local_state_change);  out:  	cfg80211_unlock_rdev(rdev); @@ -3648,6 +3656,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)  	const u8 *ie = NULL, *bssid;  	int err, ie_len = 0;  	u16 reason_code; +	bool local_state_change;  	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))  		return -EINVAL; @@ -3693,7 +3702,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)  		ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);  	} -	err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); +	local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + +	err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, +				   local_state_change);  out:  	cfg80211_unlock_rdev(rdev); @@ -3710,6 +3722,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)  	const u8 *ie = NULL, *bssid;  	int err, ie_len = 0;  	u16 reason_code; +	bool local_state_change;  	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))  		return -EINVAL; @@ -3755,7 +3768,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)  		ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);  	} -	err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); +	local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; + +	err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, +				     local_state_change);  out:  	cfg80211_unlock_rdev(rdev); @@ -4778,6 +4794,84 @@ unlock_rtnl:  	return err;  } +static struct nla_policy +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { +	[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, +	[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, +	[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, +}; + +static int nl80211_set_cqm_rssi(struct genl_info *info, +				s32 threshold, u32 hysteresis) +{ +	struct cfg80211_registered_device *rdev; +	struct wireless_dev *wdev; +	struct net_device *dev; +	int err; + +	if (threshold > 0) +		return -EINVAL; + +	rtnl_lock(); + +	err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); +	if (err) +		goto unlock_rdev; + +	wdev = dev->ieee80211_ptr; + +	if (!rdev->ops->set_cqm_rssi_config) { +		err = -EOPNOTSUPP; +		goto unlock_rdev; +	} + +	if (wdev->iftype != NL80211_IFTYPE_STATION) { +		err = -EOPNOTSUPP; +		goto unlock_rdev; +	} + +	err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, +					     threshold, hysteresis); + +unlock_rdev: +	cfg80211_unlock_rdev(rdev); +	dev_put(dev); +	rtnl_unlock(); + +	return err; +} + +static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) +{ +	struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; +	struct nlattr *cqm; +	int err; + +	cqm = info->attrs[NL80211_ATTR_CQM]; +	if (!cqm) { +		err = -EINVAL; +		goto out; +	} + +	err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, +			       nl80211_attr_cqm_policy); +	if (err) +		goto out; + +	if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && +	    attrs[NL80211_ATTR_CQM_RSSI_HYST]) { +		s32 threshold; +		u32 hysteresis; +		threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); +		hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); +		err = nl80211_set_cqm_rssi(info, threshold, hysteresis); +	} else +		err = -EINVAL; + +out: +	return err; +} +  static struct genl_ops nl80211_ops[] = {  	{  		.cmd = NL80211_CMD_GET_WIPHY, @@ -5082,6 +5176,12 @@ static struct genl_ops nl80211_ops[] = {  		.policy = nl80211_policy,  		/* can be retrieved by unprivileged users */  	}, +	{ +		.cmd = NL80211_CMD_SET_CQM, +		.doit = nl80211_set_cqm, +		.policy = nl80211_policy, +		.flags = GENL_ADMIN_PERM, +	},  };  static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -5832,6 +5932,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,  	nlmsg_free(msg);  } +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, +			     struct net_device *netdev, +			     enum nl80211_cqm_rssi_threshold_event rssi_event, +			     gfp_t gfp) +{ +	struct sk_buff *msg; +	struct nlattr *pinfoattr; +	void *hdr; + +	msg = nlmsg_new(NLMSG_GOODSIZE, gfp); +	if (!msg) +		return; + +	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); +	if (!hdr) { +		nlmsg_free(msg); +		return; +	} + +	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + +	pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); +	if (!pinfoattr) +		goto nla_put_failure; + +	NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, +		    rssi_event); + +	nla_nest_end(msg, pinfoattr); + +	if (genlmsg_end(msg, hdr) < 0) { +		nlmsg_free(msg); +		return; +	} + +	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, +				nl80211_mlme_mcgrp.id, gfp); +	return; + + nla_put_failure: +	genlmsg_cancel(msg, hdr); +	nlmsg_free(msg); +} +  static int nl80211_netlink_notify(struct notifier_block * nb,  				  unsigned long state,  				  void *_notify)  |