diff options
Diffstat (limited to 'net/wireless')
| -rw-r--r-- | net/wireless/core.c | 34 | ||||
| -rw-r--r-- | net/wireless/core.h | 11 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 735 | ||||
| -rw-r--r-- | net/wireless/nl80211.h | 16 | ||||
| -rw-r--r-- | net/wireless/reg.c | 216 | ||||
| -rw-r--r-- | net/wireless/reg.h | 1 | ||||
| -rw-r--r-- | net/wireless/scan.c | 13 | ||||
| -rw-r--r-- | net/wireless/trace.h | 31 | ||||
| -rw-r--r-- | net/wireless/util.c | 103 | ||||
| -rw-r--r-- | net/wireless/wext-compat.c | 10 | 
10 files changed, 857 insertions, 313 deletions
| diff --git a/net/wireless/core.c b/net/wireless/core.c index 53dda7728f86..3af0ecf1cc16 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -320,6 +320,20 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work)  	rtnl_unlock();  } +static void cfg80211_sched_scan_stop_wk(struct work_struct *work) +{ +	struct cfg80211_registered_device *rdev; + +	rdev = container_of(work, struct cfg80211_registered_device, +			   sched_scan_stop_wk); + +	rtnl_lock(); + +	__cfg80211_stop_sched_scan(rdev, false); + +	rtnl_unlock(); +} +  /* exported functions */  struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, @@ -406,6 +420,7 @@ use_default_name:  	INIT_LIST_HEAD(&rdev->destroy_list);  	spin_lock_init(&rdev->destroy_list_lock);  	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); +	INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk);  #ifdef CONFIG_CFG80211_DEFAULT_PS  	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -560,6 +575,14 @@ int wiphy_register(struct wiphy *wiphy)  				       BIT(NL80211_IFTYPE_MONITOR)))  		wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; +	if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && +		    (wiphy->regulatory_flags & +					(REGULATORY_CUSTOM_REG | +					 REGULATORY_STRICT_REG | +					 REGULATORY_COUNTRY_IE_FOLLOW_POWER | +					 REGULATORY_COUNTRY_IE_IGNORE)))) +		return -EINVAL; +  	if (WARN_ON(wiphy->coalesce &&  		    (!wiphy->coalesce->n_rules ||  		     !wiphy->coalesce->n_patterns) && @@ -778,6 +801,7 @@ void wiphy_unregister(struct wiphy *wiphy)  	flush_work(&rdev->event_work);  	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);  	flush_work(&rdev->destroy_work); +	flush_work(&rdev->sched_scan_stop_wk);  #ifdef CONFIG_PM  	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) @@ -858,6 +882,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,  		      struct wireless_dev *wdev)  {  	struct net_device *dev = wdev->netdev; +	struct cfg80211_sched_scan_request *sched_scan_req;  	ASSERT_RTNL();  	ASSERT_WDEV_LOCK(wdev); @@ -868,7 +893,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,  		break;  	case NL80211_IFTYPE_P2P_CLIENT:  	case NL80211_IFTYPE_STATION: -		if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) +		sched_scan_req = rtnl_dereference(rdev->sched_scan_req); +		if (sched_scan_req && dev == sched_scan_req->dev)  			__cfg80211_stop_sched_scan(rdev, false);  #ifdef CONFIG_CFG80211_WEXT @@ -943,6 +969,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev; +	struct cfg80211_sched_scan_request *sched_scan_req;  	if (!wdev)  		return NOTIFY_DONE; @@ -1007,8 +1034,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  			___cfg80211_scan_done(rdev, false);  		} -		if (WARN_ON(rdev->sched_scan_req && -			    rdev->sched_scan_req->dev == wdev->netdev)) { +		sched_scan_req = rtnl_dereference(rdev->sched_scan_req); +		if (WARN_ON(sched_scan_req && +			    sched_scan_req->dev == wdev->netdev)) {  			__cfg80211_stop_sched_scan(rdev, false);  		} diff --git a/net/wireless/core.h b/net/wireless/core.h index faa5b1609aae..801cd49c5a0c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -36,6 +36,13 @@ struct cfg80211_registered_device {  	 * the country on the country IE changed. */  	char country_ie_alpha2[2]; +	/* +	 * the driver requests the regulatory core to set this regulatory +	 * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED +	 * devices using the regulatory_set_wiphy_regd() API +	 */ +	const struct ieee80211_regdomain *requested_regd; +  	/* If a Country IE has been received this tells us the environment  	 * which its telling us its in. This defaults to ENVIRON_ANY */  	enum environment_cap env; @@ -63,7 +70,7 @@ struct cfg80211_registered_device {  	u32 bss_generation;  	struct cfg80211_scan_request *scan_req; /* protected by RTNL */  	struct sk_buff *scan_msg; -	struct cfg80211_sched_scan_request *sched_scan_req; +	struct cfg80211_sched_scan_request __rcu *sched_scan_req;  	unsigned long suspend_at;  	struct work_struct scan_done_wk;  	struct work_struct sched_scan_results_wk; @@ -84,6 +91,8 @@ struct cfg80211_registered_device {  	struct list_head destroy_list;  	struct work_struct destroy_work; +	struct work_struct sched_scan_stop_wk; +  	/* must be last because of the way we do wiphy_priv(),  	 * and it should at least be aligned to NETDEV_ALIGN */  	struct wiphy wiphy __aligned(NETDEV_ALIGN); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7ca4b5133123..d78fd8b54515 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -59,13 +59,13 @@ enum nl80211_multicast_groups {  };  static const struct genl_multicast_group nl80211_mcgrps[] = { -	[NL80211_MCGRP_CONFIG] = { .name = "config", }, -	[NL80211_MCGRP_SCAN] = { .name = "scan", }, -	[NL80211_MCGRP_REGULATORY] = { .name = "regulatory", }, -	[NL80211_MCGRP_MLME] = { .name = "mlme", }, -	[NL80211_MCGRP_VENDOR] = { .name = "vendor", }, +	[NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG }, +	[NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN }, +	[NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG }, +	[NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME }, +	[NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },  #ifdef CONFIG_NL80211_TESTMODE -	[NL80211_MCGRP_TESTMODE] = { .name = "testmode", } +	[NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }  #endif  }; @@ -396,6 +396,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {  	[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },  	[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },  	[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN }, +	[NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG }, +	[NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 }, +	[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },  };  /* policy for the key attributes */ @@ -1087,6 +1090,11 @@ static int nl80211_send_wowlan(struct sk_buff *msg,  			return -ENOBUFS;  	} +	if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) && +	    nla_put_u32(msg, NL80211_WOWLAN_TRIG_NET_DETECT, +			rdev->wiphy.wowlan->max_nd_match_sets)) +		return -ENOBUFS; +  	if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))  		return -ENOBUFS; @@ -1701,12 +1709,22 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,  			       rdev->wiphy.max_num_csa_counters))  			goto nla_put_failure; +		if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && +		    nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) +			goto nla_put_failure; + +		if (nla_put(msg, NL80211_ATTR_EXT_FEATURES, +			    sizeof(rdev->wiphy.ext_features), +			    rdev->wiphy.ext_features)) +			goto nla_put_failure; +  		/* done */  		state->split_start = 0;  		break;  	}   finish: -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -2389,7 +2407,8 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag  			goto nla_put_failure;  	} -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -2854,6 +2873,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)  	if (!rdev->ops->get_key)  		return -EOPNOTSUPP; +	if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) +		return -ENOENT; +  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg)  		return -ENOMEM; @@ -2873,10 +2895,6 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)  	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))  		goto nla_put_failure; -	if (pairwise && mac_addr && -	    !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) -		return -ENOENT; -  	err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,  			   get_key_callback); @@ -3047,7 +3065,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)  	wdev_lock(dev->ieee80211_ptr);  	err = nl80211_key_allowed(dev->ieee80211_ptr); -	if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr && +	if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&  	    !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))  		err = -ENOENT; @@ -3563,6 +3581,7 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,  	struct nlattr *rate;  	u32 bitrate;  	u16 bitrate_compat; +	enum nl80211_attrs rate_flg;  	rate = nla_nest_start(msg, attr);  	if (!rate) @@ -3579,12 +3598,36 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,  	    nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))  		return false; +	switch (info->bw) { +	case RATE_INFO_BW_5: +		rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH; +		break; +	case RATE_INFO_BW_10: +		rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH; +		break; +	default: +		WARN_ON(1); +		/* fall through */ +	case RATE_INFO_BW_20: +		rate_flg = 0; +		break; +	case RATE_INFO_BW_40: +		rate_flg = NL80211_RATE_INFO_40_MHZ_WIDTH; +		break; +	case RATE_INFO_BW_80: +		rate_flg = NL80211_RATE_INFO_80_MHZ_WIDTH; +		break; +	case RATE_INFO_BW_160: +		rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH; +		break; +	} + +	if (rate_flg && nla_put_flag(msg, rate_flg)) +		return false; +  	if (info->flags & RATE_INFO_FLAGS_MCS) {  		if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))  			return false; -		if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && -		    nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) -			return false;  		if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&  		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))  			return false; @@ -3593,18 +3636,6 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,  			return false;  		if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))  			return false; -		if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && -		    nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) -			return false; -		if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH && -		    nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH)) -			return false; -		if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH && -		    nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH)) -			return false; -		if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH && -		    nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH)) -			return false;  		if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&  		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))  			return false; @@ -3640,8 +3671,8 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,  	return true;  } -static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, -				int flags, +static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, +				u32 seq, int flags,  				struct cfg80211_registered_device *rdev,  				struct net_device *dev,  				const u8 *mac_addr, struct station_info *sinfo) @@ -3649,7 +3680,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,  	void *hdr;  	struct nlattr *sinfoattr, *bss_param; -	hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); +	hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);  	if (!hdr)  		return -1; @@ -3661,115 +3692,77 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,  	sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);  	if (!sinfoattr)  		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) && -	    nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME, -			sinfo->connected_time)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) && -	    nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME, -			sinfo->inactive_time)) -		goto nla_put_failure; -	if ((sinfo->filled & (STATION_INFO_RX_BYTES | -			      STATION_INFO_RX_BYTES64)) && + +#define PUT_SINFO(attr, memb, type) do {				\ +	if (sinfo->filled & BIT(NL80211_STA_INFO_ ## attr) &&		\ +	    nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\ +			     sinfo->memb))				\ +		goto nla_put_failure;					\ +	} while (0) + +	PUT_SINFO(CONNECTED_TIME, connected_time, u32); +	PUT_SINFO(INACTIVE_TIME, inactive_time, u32); + +	if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) | +			     BIT(NL80211_STA_INFO_RX_BYTES64)) &&  	    nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,  			(u32)sinfo->rx_bytes))  		goto nla_put_failure; -	if ((sinfo->filled & (STATION_INFO_TX_BYTES | -			      STATION_INFO_TX_BYTES64)) && + +	if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) | +			     BIT(NL80211_STA_INFO_TX_BYTES64)) &&  	    nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,  			(u32)sinfo->tx_bytes))  		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_RX_BYTES64) && -	    nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64, -			sinfo->rx_bytes)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_TX_BYTES64) && -	    nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64, -			sinfo->tx_bytes)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_LLID) && -	    nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_PLID) && -	    nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_PLINK_STATE) && -	    nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE, -		       sinfo->plink_state)) -		goto nla_put_failure; + +	PUT_SINFO(RX_BYTES64, rx_bytes, u64); +	PUT_SINFO(TX_BYTES64, tx_bytes, u64); +	PUT_SINFO(LLID, llid, u16); +	PUT_SINFO(PLID, plid, u16); +	PUT_SINFO(PLINK_STATE, plink_state, u8); +  	switch (rdev->wiphy.signal_type) {  	case CFG80211_SIGNAL_TYPE_MBM: -		if ((sinfo->filled & STATION_INFO_SIGNAL) && -		    nla_put_u8(msg, NL80211_STA_INFO_SIGNAL, -			       sinfo->signal)) -			goto nla_put_failure; -		if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) && -		    nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG, -			       sinfo->signal_avg)) -			goto nla_put_failure; +		PUT_SINFO(SIGNAL, signal, u8); +		PUT_SINFO(SIGNAL_AVG, signal_avg, u8);  		break;  	default:  		break;  	} -	if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) { +	if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) {  		if (!nl80211_put_signal(msg, sinfo->chains,  					sinfo->chain_signal,  					NL80211_STA_INFO_CHAIN_SIGNAL))  			goto nla_put_failure;  	} -	if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) { +	if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {  		if (!nl80211_put_signal(msg, sinfo->chains,  					sinfo->chain_signal_avg,  					NL80211_STA_INFO_CHAIN_SIGNAL_AVG))  			goto nla_put_failure;  	} -	if (sinfo->filled & STATION_INFO_TX_BITRATE) { +	if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {  		if (!nl80211_put_sta_rate(msg, &sinfo->txrate,  					  NL80211_STA_INFO_TX_BITRATE))  			goto nla_put_failure;  	} -	if (sinfo->filled & STATION_INFO_RX_BITRATE) { +	if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) {  		if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,  					  NL80211_STA_INFO_RX_BITRATE))  			goto nla_put_failure;  	} -	if ((sinfo->filled & STATION_INFO_RX_PACKETS) && -	    nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS, -			sinfo->rx_packets)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_TX_PACKETS) && -	    nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS, -			sinfo->tx_packets)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_TX_RETRIES) && -	    nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES, -			sinfo->tx_retries)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_TX_FAILED) && -	    nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED, -			sinfo->tx_failed)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) && -	    nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT, -			sinfo->expected_throughput)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) && -	    nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, -			sinfo->beacon_loss_count)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_LOCAL_PM) && -	    nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM, -			sinfo->local_pm)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_PEER_PM) && -	    nla_put_u32(msg, NL80211_STA_INFO_PEER_PM, -			sinfo->peer_pm)) -		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_NONPEER_PM) && -	    nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM, -			sinfo->nonpeer_pm)) -		goto nla_put_failure; -	if (sinfo->filled & STATION_INFO_BSS_PARAM) { + +	PUT_SINFO(RX_PACKETS, rx_packets, u32); +	PUT_SINFO(TX_PACKETS, tx_packets, u32); +	PUT_SINFO(TX_RETRIES, tx_retries, u32); +	PUT_SINFO(TX_FAILED, tx_failed, u32); +	PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); +	PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32); +	PUT_SINFO(LOCAL_PM, local_pm, u32); +	PUT_SINFO(PEER_PM, peer_pm, u32); +	PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); + +	if (sinfo->filled & BIT(NL80211_STA_INFO_BSS_PARAM)) {  		bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);  		if (!bss_param)  			goto nla_put_failure; @@ -3788,23 +3781,68 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,  		nla_nest_end(msg, bss_param);  	} -	if ((sinfo->filled & STATION_INFO_STA_FLAGS) && +	if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS)) &&  	    nla_put(msg, NL80211_STA_INFO_STA_FLAGS,  		    sizeof(struct nl80211_sta_flag_update),  		    &sinfo->sta_flags))  		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_T_OFFSET) && -		nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET, -			    sinfo->t_offset)) -		goto nla_put_failure; + +	PUT_SINFO(T_OFFSET, t_offset, u64); +	PUT_SINFO(RX_DROP_MISC, rx_dropped_misc, u64); +	PUT_SINFO(BEACON_RX, rx_beacon, u64); +	PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); + +#undef PUT_SINFO + +	if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) { +		struct nlattr *tidsattr; +		int tid; + +		tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS); +		if (!tidsattr) +			goto nla_put_failure; + +		for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { +			struct cfg80211_tid_stats *tidstats; +			struct nlattr *tidattr; + +			tidstats = &sinfo->pertid[tid]; + +			if (!tidstats->filled) +				continue; + +			tidattr = nla_nest_start(msg, tid + 1); +			if (!tidattr) +				goto nla_put_failure; + +#define PUT_TIDVAL(attr, memb, type) do {				\ +	if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) &&	\ +	    nla_put_ ## type(msg, NL80211_TID_STATS_ ## attr,		\ +			     tidstats->memb))				\ +		goto nla_put_failure;					\ +	} while (0) + +			PUT_TIDVAL(RX_MSDU, rx_msdu, u64); +			PUT_TIDVAL(TX_MSDU, tx_msdu, u64); +			PUT_TIDVAL(TX_MSDU_RETRIES, tx_msdu_retries, u64); +			PUT_TIDVAL(TX_MSDU_FAILED, tx_msdu_failed, u64); + +#undef PUT_TIDVAL +			nla_nest_end(msg, tidattr); +		} + +		nla_nest_end(msg, tidsattr); +	} +  	nla_nest_end(msg, sinfoattr); -	if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) && +	if (sinfo->assoc_req_ies_len &&  	    nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,  		    sinfo->assoc_req_ies))  		goto nla_put_failure; -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -3844,7 +3882,7 @@ static int nl80211_dump_station(struct sk_buff *skb,  		if (err)  			goto out_err; -		if (nl80211_send_station(skb, +		if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,  				NETLINK_CB(cb->skb).portid,  				cb->nlh->nlmsg_seq, NLM_F_MULTI,  				rdev, wdev->netdev, mac_addr, @@ -3891,7 +3929,8 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)  	if (!msg)  		return -ENOMEM; -	if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0, +	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, +				 info->snd_portid, info->snd_seq, 0,  				 rdev, dev, mac_addr, &sinfo) < 0) {  		nlmsg_free(msg);  		return -ENOBUFS; @@ -4533,7 +4572,8 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,  	nla_nest_end(msg, pinfoattr); -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -5327,42 +5367,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,  	return err;  } -static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) +static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, +			      struct sk_buff *msg)  { -	const struct ieee80211_regdomain *regdom; -	struct sk_buff *msg; -	void *hdr = NULL;  	struct nlattr *nl_reg_rules;  	unsigned int i; -	if (!cfg80211_regdomain) -		return -EINVAL; - -	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!msg) -		return -ENOBUFS; - -	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, -			     NL80211_CMD_GET_REG); -	if (!hdr) -		goto put_failure; - -	if (reg_last_request_cell_base() && -	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, -			NL80211_USER_REG_HINT_CELL_BASE)) -		goto nla_put_failure; - -	rcu_read_lock(); -	regdom = rcu_dereference(cfg80211_regdomain); -  	if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||  	    (regdom->dfs_region &&  	     nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) -		goto nla_put_failure_rcu; +		goto nla_put_failure;  	nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);  	if (!nl_reg_rules) -		goto nla_put_failure_rcu; +		goto nla_put_failure;  	for (i = 0; i < regdom->n_reg_rules; i++) {  		struct nlattr *nl_reg_rule; @@ -5377,7 +5395,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  		nl_reg_rule = nla_nest_start(msg, i);  		if (!nl_reg_rule) -			goto nla_put_failure_rcu; +			goto nla_put_failure;  		max_bandwidth_khz = freq_range->max_bandwidth_khz;  		if (!max_bandwidth_khz) @@ -5398,13 +5416,74 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  				power_rule->max_eirp) ||  		    nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,  				reg_rule->dfs_cac_ms)) -			goto nla_put_failure_rcu; +			goto nla_put_failure;  		nla_nest_end(msg, nl_reg_rule);  	} -	rcu_read_unlock();  	nla_nest_end(msg, nl_reg_rules); +	return 0; + +nla_put_failure: +	return -EMSGSIZE; +} + +static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) +{ +	const struct ieee80211_regdomain *regdom = NULL; +	struct cfg80211_registered_device *rdev; +	struct wiphy *wiphy = NULL; +	struct sk_buff *msg; +	void *hdr; + +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (!msg) +		return -ENOBUFS; + +	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, +			     NL80211_CMD_GET_REG); +	if (!hdr) +		goto put_failure; + +	if (info->attrs[NL80211_ATTR_WIPHY]) { +		bool self_managed; + +		rdev = cfg80211_get_dev_from_info(genl_info_net(info), info); +		if (IS_ERR(rdev)) { +			nlmsg_free(msg); +			return PTR_ERR(rdev); +		} + +		wiphy = &rdev->wiphy; +		self_managed = wiphy->regulatory_flags & +			       REGULATORY_WIPHY_SELF_MANAGED; +		regdom = get_wiphy_regdom(wiphy); + +		/* a self-managed-reg device must have a private regdom */ +		if (WARN_ON(!regdom && self_managed)) { +			nlmsg_free(msg); +			return -EINVAL; +		} + +		if (regdom && +		    nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) +			goto nla_put_failure; +	} + +	if (!wiphy && reg_last_request_cell_base() && +	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, +			NL80211_USER_REG_HINT_CELL_BASE)) +		goto nla_put_failure; + +	rcu_read_lock(); + +	if (!regdom) +		regdom = rcu_dereference(cfg80211_regdomain); + +	if (nl80211_put_regdom(regdom, msg)) +		goto nla_put_failure_rcu; + +	rcu_read_unlock();  	genlmsg_end(msg, hdr);  	return genlmsg_reply(msg, info); @@ -5418,6 +5497,84 @@ put_failure:  	return -EMSGSIZE;  } +static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, +			       u32 seq, int flags, struct wiphy *wiphy, +			       const struct ieee80211_regdomain *regdom) +{ +	void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags, +				   NL80211_CMD_GET_REG); + +	if (!hdr) +		return -1; + +	genl_dump_check_consistent(cb, hdr, &nl80211_fam); + +	if (nl80211_put_regdom(regdom, msg)) +		goto nla_put_failure; + +	if (!wiphy && reg_last_request_cell_base() && +	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, +			NL80211_USER_REG_HINT_CELL_BASE)) +		goto nla_put_failure; + +	if (wiphy && +	    nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy))) +		goto nla_put_failure; + +	if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && +	    nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) +		goto nla_put_failure; + +	genlmsg_end(msg, hdr); +	return 0; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +	return -EMSGSIZE; +} + +static int nl80211_get_reg_dump(struct sk_buff *skb, +				struct netlink_callback *cb) +{ +	const struct ieee80211_regdomain *regdom = NULL; +	struct cfg80211_registered_device *rdev; +	int err, reg_idx, start = cb->args[2]; + +	rtnl_lock(); + +	if (cfg80211_regdomain && start == 0) { +		err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, +					  NLM_F_MULTI, NULL, +					  rtnl_dereference(cfg80211_regdomain)); +		if (err < 0) +			goto out_err; +	} + +	/* the global regdom is idx 0 */ +	reg_idx = 1; +	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { +		regdom = get_wiphy_regdom(&rdev->wiphy); +		if (!regdom) +			continue; + +		if (++reg_idx <= start) +			continue; + +		err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq, +					  NLM_F_MULTI, &rdev->wiphy, regdom); +		if (err < 0) { +			reg_idx--; +			break; +		} +	} + +	cb->args[2] = reg_idx; +	err = skb->len; +out_err: +	rtnl_unlock(); +	return err; +} +  static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  {  	struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; @@ -5623,7 +5780,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)  		request->ssids = (void *)&request->channels[n_channels];  	request->n_ssids = n_ssids;  	if (ie_len) { -		if (request->ssids) +		if (n_ssids)  			request->ie = (void *)(request->ssids + n_ssids);  		else  			request->ie = (void *)(request->channels + n_channels); @@ -5679,7 +5836,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)  	request->n_channels = i;  	i = 0; -	if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { +	if (n_ssids) {  		nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {  			if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {  				err = -EINVAL; @@ -5877,7 +6034,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,  		request->ssids = (void *)&request->channels[n_channels];  	request->n_ssids = n_ssids;  	if (ie_len) { -		if (request->ssids) +		if (n_ssids)  			request->ie = (void *)(request->ssids + n_ssids);  		else  			request->ie = (void *)(request->channels + n_channels); @@ -5886,7 +6043,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,  	if (n_match_sets) {  		if (request->ie)  			request->match_sets = (void *)(request->ie + ie_len); -		else if (request->ssids) +		else if (n_ssids)  			request->match_sets =  				(void *)(request->ssids + n_ssids);  		else @@ -5945,7 +6102,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,  	request->n_channels = i;  	i = 0; -	if (attrs[NL80211_ATTR_SCAN_SSIDS]) { +	if (n_ssids) {  		nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],  				    tmp) {  			if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { @@ -6053,6 +6210,10 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,  		}  	} +	if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY]) +		request->delay = +			nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]); +  	request->interval = interval;  	request->scan_start = jiffies; @@ -6069,6 +6230,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct net_device *dev = info->user_ptr[1];  	struct wireless_dev *wdev = dev->ieee80211_ptr; +	struct cfg80211_sched_scan_request *sched_scan_req;  	int err;  	if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || @@ -6078,27 +6240,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  	if (rdev->sched_scan_req)  		return -EINPROGRESS; -	rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, -							info->attrs); -	err = PTR_ERR_OR_ZERO(rdev->sched_scan_req); +	sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, +						  info->attrs); + +	err = PTR_ERR_OR_ZERO(sched_scan_req);  	if (err)  		goto out_err; -	err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req); +	err = rdev_sched_scan_start(rdev, dev, sched_scan_req);  	if (err)  		goto out_free; -	rdev->sched_scan_req->dev = dev; -	rdev->sched_scan_req->wiphy = &rdev->wiphy; +	sched_scan_req->dev = dev; +	sched_scan_req->wiphy = &rdev->wiphy; + +	if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) +		sched_scan_req->owner_nlportid = info->snd_portid; + +	rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);  	nl80211_send_sched_scan(rdev, dev,  				NL80211_CMD_START_SCHED_SCAN);  	return 0;  out_free: -	kfree(rdev->sched_scan_req); +	kfree(sched_scan_req);  out_err: -	rdev->sched_scan_req = NULL;  	return err;  } @@ -6433,7 +6600,8 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,  	nla_nest_end(msg, bss); -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   fail_unlock_rcu:  	rcu_read_unlock(); @@ -6481,12 +6649,17 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)  }  static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, -				int flags, struct net_device *dev, -				struct survey_info *survey) +			       int flags, struct net_device *dev, +			       bool allow_radio_stats, +			       struct survey_info *survey)  {  	void *hdr;  	struct nlattr *infoattr; +	/* skip radio stats if userspace didn't request them */ +	if (!survey->channel && !allow_radio_stats) +		return 0; +  	hdr = nl80211hdr_put(msg, portid, seq, flags,  			     NL80211_CMD_NEW_SURVEY_RESULTS);  	if (!hdr) @@ -6499,7 +6672,8 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,  	if (!infoattr)  		goto nla_put_failure; -	if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY, +	if (survey->channel && +	    nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,  			survey->channel->center_freq))  		goto nla_put_failure; @@ -6509,49 +6683,57 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,  	if ((survey->filled & SURVEY_INFO_IN_USE) &&  	    nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))  		goto nla_put_failure; -	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) && -	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, -			survey->channel_time)) +	if ((survey->filled & SURVEY_INFO_TIME) && +	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME, +			survey->time)) +		goto nla_put_failure; +	if ((survey->filled & SURVEY_INFO_TIME_BUSY) && +	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_BUSY, +			survey->time_busy))  		goto nla_put_failure; -	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) && -	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, -			survey->channel_time_busy)) +	if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) && +	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY, +			survey->time_ext_busy))  		goto nla_put_failure; -	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) && -	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, -			survey->channel_time_ext_busy)) +	if ((survey->filled & SURVEY_INFO_TIME_RX) && +	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_RX, +			survey->time_rx))  		goto nla_put_failure; -	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) && -	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, -			survey->channel_time_rx)) +	if ((survey->filled & SURVEY_INFO_TIME_TX) && +	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX, +			survey->time_tx))  		goto nla_put_failure; -	if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) && -	    nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, -			survey->channel_time_tx)) +	if ((survey->filled & SURVEY_INFO_TIME_SCAN) && +	    nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_SCAN, +			survey->time_scan))  		goto nla_put_failure;  	nla_nest_end(msg, infoattr); -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr);  	return -EMSGSIZE;  } -static int nl80211_dump_survey(struct sk_buff *skb, -			struct netlink_callback *cb) +static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)  {  	struct survey_info survey;  	struct cfg80211_registered_device *rdev;  	struct wireless_dev *wdev;  	int survey_idx = cb->args[2];  	int res; +	bool radio_stats;  	res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);  	if (res)  		return res; +	/* prepare_wdev_dump parsed the attributes */ +	radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; +  	if (!wdev->netdev) {  		res = -EINVAL;  		goto out_err; @@ -6569,13 +6751,9 @@ static int nl80211_dump_survey(struct sk_buff *skb,  		if (res)  			goto out_err; -		/* Survey without a channel doesn't make sense */ -		if (!survey.channel) { -			res = -EINVAL; -			goto out; -		} - -		if (survey.channel->flags & IEEE80211_CHAN_DISABLED) { +		/* don't send disabled channels, but do send non-channel data */ +		if (survey.channel && +		    survey.channel->flags & IEEE80211_CHAN_DISABLED) {  			survey_idx++;  			continue;  		} @@ -6583,7 +6761,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,  		if (nl80211_send_survey(skb,  				NETLINK_CB(cb->skb).portid,  				cb->nlh->nlmsg_seq, NLM_F_MULTI, -				wdev->netdev, &survey) < 0) +				wdev->netdev, radio_stats, &survey) < 0)  			goto out;  		survey_idx++;  	} @@ -7596,14 +7774,19 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct net *net;  	int err; -	u32 pid; -	if (!info->attrs[NL80211_ATTR_PID]) -		return -EINVAL; +	if (info->attrs[NL80211_ATTR_PID]) { +		u32 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); -	pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); +		net = get_net_ns_by_pid(pid); +	} else if (info->attrs[NL80211_ATTR_NETNS_FD]) { +		u32 fd = nla_get_u32(info->attrs[NL80211_ATTR_NETNS_FD]); + +		net = get_net_ns_by_fd(fd); +	} else { +		return -EINVAL; +	} -	net = get_net_ns_by_pid(pid);  	if (IS_ERR(net))  		return PTR_ERR(net); @@ -8599,6 +8782,48 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg,  	return 0;  } +static int nl80211_send_wowlan_nd(struct sk_buff *msg, +				  struct cfg80211_sched_scan_request *req) +{ +	struct nlattr *nd, *freqs, *matches, *match; +	int i; + +	if (!req) +		return 0; + +	nd = nla_nest_start(msg, NL80211_WOWLAN_TRIG_NET_DETECT); +	if (!nd) +		return -ENOBUFS; + +	if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval)) +		return -ENOBUFS; + +	freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); +	if (!freqs) +		return -ENOBUFS; + +	for (i = 0; i < req->n_channels; i++) +		nla_put_u32(msg, i, req->channels[i]->center_freq); + +	nla_nest_end(msg, freqs); + +	if (req->n_match_sets) { +		matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); +		for (i = 0; i < req->n_match_sets; i++) { +			match = nla_nest_start(msg, i); +			nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID, +				req->match_sets[i].ssid.ssid_len, +				req->match_sets[i].ssid.ssid); +			nla_nest_end(msg, match); +		} +		nla_nest_end(msg, matches); +	} + +	nla_nest_end(msg, nd); + +	return 0; +} +  static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -8656,6 +8881,11 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)  					    rdev->wiphy.wowlan_config->tcp))  			goto nla_put_failure; +		if (nl80211_send_wowlan_nd( +			    msg, +			    rdev->wiphy.wowlan_config->nd_config)) +			goto nla_put_failure; +  		nla_nest_end(msg, nl_wowlan);  	} @@ -10225,7 +10455,8 @@ static const struct genl_ops nl80211_ops[] = {  	},  	{  		.cmd = NL80211_CMD_GET_REG, -		.doit = nl80211_get_reg, +		.doit = nl80211_get_reg_do, +		.dumpit = nl80211_get_reg_dump,  		.policy = nl80211_policy,  		.internal_flags = NL80211_FLAG_NEED_RTNL,  		/* can be retrieved by unprivileged users */ @@ -10824,7 +11055,8 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,  	/* ignore errors and send incomplete event anyway */  	nl80211_add_scan_req(msg, rdev); -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -10847,7 +11079,8 @@ nl80211_send_sched_scan_msg(struct sk_buff *msg,  	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))  		goto nla_put_failure; -	return genlmsg_end(msg, hdr); +	genlmsg_end(msg, hdr); +	return 0;   nla_put_failure:  	genlmsg_cancel(msg, hdr); @@ -10939,25 +11172,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,  				NL80211_MCGRP_SCAN, GFP_KERNEL);  } -/* - * This can happen on global regulatory changes or device specific settings - * based on custom world regulatory domains. - */ -void nl80211_send_reg_change_event(struct regulatory_request *request) +static bool nl80211_reg_change_event_fill(struct sk_buff *msg, +					  struct regulatory_request *request)  { -	struct sk_buff *msg; -	void *hdr; - -	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!msg) -		return; - -	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); -	if (!hdr) { -		nlmsg_free(msg); -		return; -	} -  	/* Userspace can always count this one always being set */  	if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))  		goto nla_put_failure; @@ -10983,8 +11200,46 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)  			goto nla_put_failure;  	} -	if (request->wiphy_idx != WIPHY_IDX_INVALID && -	    nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) +	if (request->wiphy_idx != WIPHY_IDX_INVALID) { +		struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx); + +		if (wiphy && +		    nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) +			goto nla_put_failure; + +		if (wiphy && +		    wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && +		    nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) +			goto nla_put_failure; +	} + +	return true; + +nla_put_failure: +	return false; +} + +/* + * This can happen on global regulatory changes or device specific settings + * based on custom regulatory domains. + */ +void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, +				     struct regulatory_request *request) +{ +	struct sk_buff *msg; +	void *hdr; + +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (!msg) +		return; + +	hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id); +	if (!hdr) { +		nlmsg_free(msg); +		return; +	} + +	if (nl80211_reg_change_event_fill(msg, request) == false)  		goto nla_put_failure;  	genlmsg_end(msg, hdr); @@ -11523,7 +11778,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,  	if (!msg)  		return; -	if (nl80211_send_station(msg, 0, 0, 0, +	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,  				 rdev, dev, mac_addr, sinfo) < 0) {  		nlmsg_free(msg);  		return; @@ -11534,12 +11789,16 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,  }  EXPORT_SYMBOL(cfg80211_new_sta); -void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) +void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, +			    struct station_info *sinfo, gfp_t gfp)  {  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg; -	void *hdr; +	struct station_info empty_sinfo = {}; + +	if (!sinfo) +		sinfo = &empty_sinfo;  	trace_cfg80211_del_sta(dev, mac_addr); @@ -11547,27 +11806,16 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)  	if (!msg)  		return; -	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); -	if (!hdr) { +	if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, +				 rdev, dev, mac_addr, sinfo) < 0) {  		nlmsg_free(msg);  		return;  	} -	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || -	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr)) -		goto nla_put_failure; - -	genlmsg_end(msg, hdr); -  	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,  				NL80211_MCGRP_MLME, gfp); -	return; - - nla_put_failure: -	genlmsg_cancel(msg, hdr); -	nlmsg_free(msg);  } -EXPORT_SYMBOL(cfg80211_del_sta); +EXPORT_SYMBOL(cfg80211_del_sta_sinfo);  void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,  			  enum nl80211_connect_failed_reason reason, @@ -12471,6 +12719,13 @@ static int nl80211_netlink_notify(struct notifier_block * nb,  	list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {  		bool schedule_destroy_work = false; +		bool schedule_scan_stop = false; +		struct cfg80211_sched_scan_request *sched_scan_req = +			rcu_dereference(rdev->sched_scan_req); + +		if (sched_scan_req && notify->portid && +		    sched_scan_req->owner_nlportid == notify->portid) +			schedule_scan_stop = true;  		list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {  			cfg80211_mlme_unregister_socket(wdev, notify->portid); @@ -12501,6 +12756,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb,  				spin_unlock(&rdev->destroy_list_lock);  				schedule_work(&rdev->destroy_work);  			} +		} else if (schedule_scan_stop) { +			sched_scan_req->owner_nlportid = 0; + +			if (rdev->ops->sched_scan_stop && +			    rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) +				schedule_work(&rdev->sched_scan_stop_wk);  		}  	} diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 7ad70d6f0cc6..84d4edf1d545 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -17,7 +17,21 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,  			     struct net_device *netdev, u32 cmd);  void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,  				     struct net_device *netdev); -void nl80211_send_reg_change_event(struct regulatory_request *request); +void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, +				     struct regulatory_request *request); + +static inline void +nl80211_send_reg_change_event(struct regulatory_request *request) +{ +	nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request); +} + +static inline void +nl80211_send_wiphy_reg_change_event(struct regulatory_request *request) +{ +	nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request); +} +  void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,  			  struct net_device *netdev,  			  const u8 *buf, size_t len, gfp_t gfp); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7b8309840d4e..b586d0dcb09e 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -109,7 +109,7 @@ static struct regulatory_request core_request_world = {   * protected by RTNL (and can be accessed with RCU protection)   */  static struct regulatory_request __rcu *last_request = -	(void __rcu *)&core_request_world; +	(void __force __rcu *)&core_request_world;  /* To trigger userspace events */  static struct platform_device *reg_pdev; @@ -142,7 +142,7 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void)  	return rtnl_dereference(cfg80211_regdomain);  } -static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) +const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)  {  	return rtnl_dereference(wiphy->regd);  } @@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,  {  	struct regulatory_request *lr = get_last_request(); +	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) +		return true; +  	if (!lr) {  		REG_DBG_PRINT("Ignoring regulatory request set by %s "  			      "since last_request is not set\n", @@ -1530,45 +1533,40 @@ static void reg_call_notifier(struct wiphy *wiphy,  static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)  { -	struct ieee80211_channel *ch;  	struct cfg80211_chan_def chandef;  	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); -	bool ret = true; +	enum nl80211_iftype iftype;  	wdev_lock(wdev); +	iftype = wdev->iftype; +	/* make sure the interface is active */  	if (!wdev->netdev || !netif_running(wdev->netdev)) -		goto out; +		goto wdev_inactive_unlock; -	switch (wdev->iftype) { +	switch (iftype) {  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_P2P_GO:  		if (!wdev->beacon_interval) -			goto out; - -		ret = cfg80211_reg_can_beacon(wiphy, -					      &wdev->chandef, wdev->iftype); +			goto wdev_inactive_unlock; +		chandef = wdev->chandef;  		break;  	case NL80211_IFTYPE_ADHOC:  		if (!wdev->ssid_len) -			goto out; - -		ret = cfg80211_reg_can_beacon(wiphy, -					      &wdev->chandef, wdev->iftype); +			goto wdev_inactive_unlock; +		chandef = wdev->chandef;  		break;  	case NL80211_IFTYPE_STATION:  	case NL80211_IFTYPE_P2P_CLIENT:  		if (!wdev->current_bss ||  		    !wdev->current_bss->pub.channel) -			goto out; +			goto wdev_inactive_unlock; -		ch = wdev->current_bss->pub.channel; -		if (rdev->ops->get_channel && -		    !rdev_get_channel(rdev, wdev, &chandef)) -			ret = cfg80211_chandef_usable(wiphy, &chandef, -						      IEEE80211_CHAN_DISABLED); -		else -			ret = !(ch->flags & IEEE80211_CHAN_DISABLED); +		if (!rdev->ops->get_channel || +		    rdev_get_channel(rdev, wdev, &chandef)) +			cfg80211_chandef_create(&chandef, +						wdev->current_bss->pub.channel, +						NL80211_CHAN_NO_HT);  		break;  	case NL80211_IFTYPE_MONITOR:  	case NL80211_IFTYPE_AP_VLAN: @@ -1581,9 +1579,26 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)  		break;  	} -out:  	wdev_unlock(wdev); -	return ret; + +	switch (iftype) { +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_P2P_GO: +	case NL80211_IFTYPE_ADHOC: +		return cfg80211_reg_can_beacon(wiphy, &chandef, iftype); +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_P2P_CLIENT: +		return cfg80211_chandef_usable(wiphy, &chandef, +					       IEEE80211_CHAN_DISABLED); +	default: +		break; +	} + +	return true; + +wdev_inactive_unlock: +	wdev_unlock(wdev); +	return true;  }  static void reg_leave_invalid_chans(struct wiphy *wiphy) @@ -1683,8 +1698,12 @@ static void handle_channel_custom(struct wiphy *wiphy,  	if (IS_ERR(reg_rule)) {  		REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",  			      chan->center_freq); -		chan->orig_flags |= IEEE80211_CHAN_DISABLED; -		chan->flags = chan->orig_flags; +		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { +			chan->flags |= IEEE80211_CHAN_DISABLED; +		} else { +			chan->orig_flags |= IEEE80211_CHAN_DISABLED; +			chan->flags = chan->orig_flags; +		}  		return;  	} @@ -1709,7 +1728,13 @@ static void handle_channel_custom(struct wiphy *wiphy,  	chan->dfs_state = NL80211_DFS_USABLE;  	chan->beacon_found = false; -	chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; + +	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) +		chan->flags = chan->orig_flags | bw_flags | +			      map_regdom_flags(reg_rule->flags); +	else +		chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; +  	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);  	chan->max_reg_power = chan->max_power =  		(int) MBM_TO_DBM(power_rule->max_eirp); @@ -2095,6 +2120,26 @@ out_free:  	reg_free_request(reg_request);  } +static bool reg_only_self_managed_wiphys(void) +{ +	struct cfg80211_registered_device *rdev; +	struct wiphy *wiphy; +	bool self_managed_found = false; + +	ASSERT_RTNL(); + +	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { +		wiphy = &rdev->wiphy; +		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) +			self_managed_found = true; +		else +			return false; +	} + +	/* make sure at least one self-managed wiphy exists */ +	return self_managed_found; +} +  /*   * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*   * Regulatory hints come on a first come first serve basis and we @@ -2126,6 +2171,11 @@ static void reg_process_pending_hints(void)  	spin_unlock(®_requests_lock); +	if (reg_only_self_managed_wiphys()) { +		reg_free_request(reg_request); +		return; +	} +  	reg_process_hint(reg_request);  } @@ -2153,11 +2203,52 @@ static void reg_process_pending_beacon_hints(void)  	spin_unlock_bh(®_pending_beacons_lock);  } +static void reg_process_self_managed_hints(void) +{ +	struct cfg80211_registered_device *rdev; +	struct wiphy *wiphy; +	const struct ieee80211_regdomain *tmp; +	const struct ieee80211_regdomain *regd; +	enum ieee80211_band band; +	struct regulatory_request request = {}; + +	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { +		wiphy = &rdev->wiphy; + +		spin_lock(®_requests_lock); +		regd = rdev->requested_regd; +		rdev->requested_regd = NULL; +		spin_unlock(®_requests_lock); + +		if (regd == NULL) +			continue; + +		tmp = get_wiphy_regdom(wiphy); +		rcu_assign_pointer(wiphy->regd, regd); +		rcu_free_regdom(tmp); + +		for (band = 0; band < IEEE80211_NUM_BANDS; band++) +			handle_band_custom(wiphy, wiphy->bands[band], regd); + +		reg_process_ht_flags(wiphy); + +		request.wiphy_idx = get_wiphy_idx(wiphy); +		request.alpha2[0] = regd->alpha2[0]; +		request.alpha2[1] = regd->alpha2[1]; +		request.initiator = NL80211_REGDOM_SET_BY_DRIVER; + +		nl80211_send_wiphy_reg_change_event(&request); +	} + +	reg_check_channels(); +} +  static void reg_todo(struct work_struct *work)  {  	rtnl_lock();  	reg_process_pending_hints();  	reg_process_pending_beacon_hints(); +	reg_process_self_managed_hints();  	rtnl_unlock();  } @@ -2438,6 +2529,8 @@ static void restore_regulatory_settings(bool reset_user)  	world_alpha2[1] = cfg80211_world_regdom->alpha2[1];  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { +		if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) +			continue;  		if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)  			restore_custom_reg_settings(&rdev->wiphy);  	} @@ -2841,10 +2934,79 @@ int set_regdom(const struct ieee80211_regdomain *rd)  	return 0;  } +static int __regulatory_set_wiphy_regd(struct wiphy *wiphy, +				       struct ieee80211_regdomain *rd) +{ +	const struct ieee80211_regdomain *regd; +	const struct ieee80211_regdomain *prev_regd; +	struct cfg80211_registered_device *rdev; + +	if (WARN_ON(!wiphy || !rd)) +		return -EINVAL; + +	if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED), +		 "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n")) +		return -EPERM; + +	if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) { +		print_regdomain_info(rd); +		return -EINVAL; +	} + +	regd = reg_copy_regd(rd); +	if (IS_ERR(regd)) +		return PTR_ERR(regd); + +	rdev = wiphy_to_rdev(wiphy); + +	spin_lock(®_requests_lock); +	prev_regd = rdev->requested_regd; +	rdev->requested_regd = regd; +	spin_unlock(®_requests_lock); + +	kfree(prev_regd); +	return 0; +} + +int regulatory_set_wiphy_regd(struct wiphy *wiphy, +			      struct ieee80211_regdomain *rd) +{ +	int ret = __regulatory_set_wiphy_regd(wiphy, rd); + +	if (ret) +		return ret; + +	schedule_work(®_work); +	return 0; +} +EXPORT_SYMBOL(regulatory_set_wiphy_regd); + +int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy, +					struct ieee80211_regdomain *rd) +{ +	int ret; + +	ASSERT_RTNL(); + +	ret = __regulatory_set_wiphy_regd(wiphy, rd); +	if (ret) +		return ret; + +	/* process the request immediately */ +	reg_process_self_managed_hints(); +	return 0; +} +EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl); +  void wiphy_regulatory_register(struct wiphy *wiphy)  {  	struct regulatory_request *lr; +	/* self-managed devices ignore external hints */ +	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) +		wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | +					   REGULATORY_COUNTRY_IE_IGNORE; +  	if (!reg_dev_ignore_cell_hint(wiphy))  		reg_num_devs_support_basehint++; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 5e48031ccb9a..4b45d6e61d24 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -38,6 +38,7 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,  				   const struct ieee80211_reg_rule *rule);  bool reg_last_request_cell_base(void); +const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);  /**   * regulatory_hint_found_beacon - hints a beacon was found on a channel diff --git a/net/wireless/scan.c b/net/wireless/scan.c index bda39f149810..c705c3e2b751 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -257,7 +257,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)  	rtnl_lock(); -	request = rdev->sched_scan_req; +	request = rtnl_dereference(rdev->sched_scan_req);  	/* we don't have sched_scan_req anymore if the scan is stopping */  	if (request) { @@ -279,7 +279,8 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy)  {  	trace_cfg80211_sched_scan_results(wiphy);  	/* ignore if we're not scanning */ -	if (wiphy_to_rdev(wiphy)->sched_scan_req) + +	if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req))  		queue_work(cfg80211_wq,  			   &wiphy_to_rdev(wiphy)->sched_scan_results_wk);  } @@ -308,6 +309,7 @@ EXPORT_SYMBOL(cfg80211_sched_scan_stopped);  int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  			       bool driver_initiated)  { +	struct cfg80211_sched_scan_request *sched_scan_req;  	struct net_device *dev;  	ASSERT_RTNL(); @@ -315,7 +317,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  	if (!rdev->sched_scan_req)  		return -ENOENT; -	dev = rdev->sched_scan_req->dev; +	sched_scan_req = rtnl_dereference(rdev->sched_scan_req); +	dev = sched_scan_req->dev;  	if (!driver_initiated) {  		int err = rdev_sched_scan_stop(rdev, dev); @@ -325,8 +328,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  	nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); -	kfree(rdev->sched_scan_req); -	rdev->sched_scan_req = NULL; +	RCU_INIT_POINTER(rdev->sched_scan_req, NULL); +	kfree_rcu(sched_scan_req, rcu_head);  	return 0;  } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ad38910f7036..b17b3692f8c2 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1604,11 +1604,12 @@ TRACE_EVENT(rdev_return_int_survey_info,  		WIPHY_ENTRY  		CHAN_ENTRY  		__field(int, ret) -		__field(u64, channel_time) -		__field(u64, channel_time_busy) -		__field(u64, channel_time_ext_busy) -		__field(u64, channel_time_rx) -		__field(u64, channel_time_tx) +		__field(u64, time) +		__field(u64, time_busy) +		__field(u64, time_ext_busy) +		__field(u64, time_rx) +		__field(u64, time_tx) +		__field(u64, time_scan)  		__field(u32, filled)  		__field(s8, noise)  	), @@ -1616,22 +1617,24 @@ TRACE_EVENT(rdev_return_int_survey_info,  		WIPHY_ASSIGN;  		CHAN_ASSIGN(info->channel);  		__entry->ret = ret; -		__entry->channel_time = info->channel_time; -		__entry->channel_time_busy = info->channel_time_busy; -		__entry->channel_time_ext_busy = info->channel_time_ext_busy; -		__entry->channel_time_rx = info->channel_time_rx; -		__entry->channel_time_tx = info->channel_time_tx; +		__entry->time = info->time; +		__entry->time_busy = info->time_busy; +		__entry->time_ext_busy = info->time_ext_busy; +		__entry->time_rx = info->time_rx; +		__entry->time_tx = info->time_tx; +		__entry->time_scan = info->time_scan;  		__entry->filled = info->filled;  		__entry->noise = info->noise;  	),  	TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT  		  ", channel time: %llu, channel time busy: %llu, "  		  "channel time extension busy: %llu, channel time rx: %llu, " -		  "channel time tx: %llu, filled: %u, noise: %d", +		  "channel time tx: %llu, scan time: %llu, filled: %u, noise: %d",  		  WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG, -		  __entry->channel_time, __entry->channel_time_busy, -		  __entry->channel_time_ext_busy, __entry->channel_time_rx, -		  __entry->channel_time_tx, __entry->filled, __entry->noise) +		  __entry->time, __entry->time_busy, +		  __entry->time_ext_busy, __entry->time_rx, +		  __entry->time_tx, __entry->time_scan, +		  __entry->filled, __entry->noise)  );  TRACE_EVENT(rdev_tdls_oper, diff --git a/net/wireless/util.c b/net/wireless/util.c index d0ac795445b7..6903dbdcb8c1 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -227,18 +227,32 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,  	if (pairwise && !mac_addr)  		return -EINVAL; -	/* -	 * Disallow pairwise keys with non-zero index unless it's WEP -	 * or a vendor specific cipher (because current deployments use -	 * pairwise WEP keys with non-zero indices and for vendor specific -	 * ciphers this should be validated in the driver or hardware level -	 * - but 802.11i clearly specifies to use zero) -	 */ -	if (pairwise && key_idx && -	    ((params->cipher == WLAN_CIPHER_SUITE_TKIP) || -	     (params->cipher == WLAN_CIPHER_SUITE_CCMP) || -	     (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC))) -		return -EINVAL; +	switch (params->cipher) { +	case WLAN_CIPHER_SUITE_TKIP: +	case WLAN_CIPHER_SUITE_CCMP: +	case WLAN_CIPHER_SUITE_CCMP_256: +	case WLAN_CIPHER_SUITE_GCMP: +	case WLAN_CIPHER_SUITE_GCMP_256: +		/* Disallow pairwise keys with non-zero index unless it's WEP +		 * or a vendor specific cipher (because current deployments use +		 * pairwise WEP keys with non-zero indices and for vendor +		 * specific ciphers this should be validated in the driver or +		 * hardware level - but 802.11i clearly specifies to use zero) +		 */ +		if (pairwise && key_idx) +			return -EINVAL; +		break; +	case WLAN_CIPHER_SUITE_AES_CMAC: +	case WLAN_CIPHER_SUITE_BIP_CMAC_256: +	case WLAN_CIPHER_SUITE_BIP_GMAC_128: +	case WLAN_CIPHER_SUITE_BIP_GMAC_256: +		/* Disallow BIP (group-only) cipher as pairwise cipher */ +		if (pairwise) +			return -EINVAL; +		break; +	default: +		break; +	}  	switch (params->cipher) {  	case WLAN_CIPHER_SUITE_WEP40: @@ -253,6 +267,18 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,  		if (params->key_len != WLAN_KEY_LEN_CCMP)  			return -EINVAL;  		break; +	case WLAN_CIPHER_SUITE_CCMP_256: +		if (params->key_len != WLAN_KEY_LEN_CCMP_256) +			return -EINVAL; +		break; +	case WLAN_CIPHER_SUITE_GCMP: +		if (params->key_len != WLAN_KEY_LEN_GCMP) +			return -EINVAL; +		break; +	case WLAN_CIPHER_SUITE_GCMP_256: +		if (params->key_len != WLAN_KEY_LEN_GCMP_256) +			return -EINVAL; +		break;  	case WLAN_CIPHER_SUITE_WEP104:  		if (params->key_len != WLAN_KEY_LEN_WEP104)  			return -EINVAL; @@ -261,6 +287,18 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,  		if (params->key_len != WLAN_KEY_LEN_AES_CMAC)  			return -EINVAL;  		break; +	case WLAN_CIPHER_SUITE_BIP_CMAC_256: +		if (params->key_len != WLAN_KEY_LEN_BIP_CMAC_256) +			return -EINVAL; +		break; +	case WLAN_CIPHER_SUITE_BIP_GMAC_128: +		if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_128) +			return -EINVAL; +		break; +	case WLAN_CIPHER_SUITE_BIP_GMAC_256: +		if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_256) +			return -EINVAL; +		break;  	default:  		/*  		 * We don't know anything about this algorithm, @@ -280,7 +318,13 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,  			return -EINVAL;  		case WLAN_CIPHER_SUITE_TKIP:  		case WLAN_CIPHER_SUITE_CCMP: +		case WLAN_CIPHER_SUITE_CCMP_256: +		case WLAN_CIPHER_SUITE_GCMP: +		case WLAN_CIPHER_SUITE_GCMP_256:  		case WLAN_CIPHER_SUITE_AES_CMAC: +		case WLAN_CIPHER_SUITE_BIP_CMAC_256: +		case WLAN_CIPHER_SUITE_BIP_GMAC_128: +		case WLAN_CIPHER_SUITE_BIP_GMAC_256:  			if (params->seq_len != 6)  				return -EINVAL;  			break; @@ -308,6 +352,12 @@ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc)  		goto out;  	} +	if (ieee80211_is_mgmt(fc)) { +		if (ieee80211_has_order(fc)) +			hdrlen += IEEE80211_HT_CTL_LEN; +		goto out; +	} +  	if (ieee80211_is_ctl(fc)) {  		/*  		 * ACK and CTS are 10 bytes, all others 16. To see how @@ -708,8 +758,8 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb,  	if (skb->priority >= 256 && skb->priority <= 263)  		return skb->priority - 256; -	if (vlan_tx_tag_present(skb)) { -		vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK) +	if (skb_vlan_tag_present(skb)) { +		vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK)  			>> VLAN_PRIO_SHIFT;  		if (vlan_priority > 0)  			return vlan_priority; @@ -1073,10 +1123,24 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)  	if (WARN_ON_ONCE(rate->mcs > 9))  		return 0; -	idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH | -			     RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 : -		  rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 : -		  rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0; +	switch (rate->bw) { +	case RATE_INFO_BW_160: +		idx = 3; +		break; +	case RATE_INFO_BW_80: +		idx = 2; +		break; +	case RATE_INFO_BW_40: +		idx = 1; +		break; +	case RATE_INFO_BW_5: +	case RATE_INFO_BW_10: +	default: +		WARN_ON(1); +		/* fall through */ +	case RATE_INFO_BW_20: +		idx = 0; +	}  	bitrate = base[idx][rate->mcs];  	bitrate *= rate->nss; @@ -1107,8 +1171,7 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)  	modulation = rate->mcs & 7;  	streams = (rate->mcs >> 3) + 1; -	bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? -			13500000 : 6500000; +	bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000;  	if (modulation < 4)  		bitrate *= (modulation + 1); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 0f47948c572f..5b24d39d7903 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1300,7 +1300,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,  	if (err)  		return err; -	if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) +	if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)))  		return -EOPNOTSUPP;  	rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); @@ -1340,7 +1340,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)  	switch (rdev->wiphy.signal_type) {  	case CFG80211_SIGNAL_TYPE_MBM: -		if (sinfo.filled & STATION_INFO_SIGNAL) { +		if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) {  			int sig = sinfo.signal;  			wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;  			wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; @@ -1354,7 +1354,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)  			break;  		}  	case CFG80211_SIGNAL_TYPE_UNSPEC: -		if (sinfo.filled & STATION_INFO_SIGNAL) { +		if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL)) {  			wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;  			wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;  			wstats.qual.level = sinfo.signal; @@ -1367,9 +1367,9 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)  	}  	wstats.qual.updated |= IW_QUAL_NOISE_INVALID; -	if (sinfo.filled & STATION_INFO_RX_DROP_MISC) +	if (sinfo.filled & BIT(NL80211_STA_INFO_RX_DROP_MISC))  		wstats.discard.misc = sinfo.rx_dropped_misc; -	if (sinfo.filled & STATION_INFO_TX_FAILED) +	if (sinfo.filled & BIT(NL80211_STA_INFO_TX_FAILED))  		wstats.discard.retries = sinfo.tx_failed;  	return &wstats; |