diff options
Diffstat (limited to 'net/wireless/util.c')
| -rw-r--r-- | net/wireless/util.c | 128 | 
1 files changed, 98 insertions, 30 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c index 5ea12afc7706..e9d040d29846 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -13,6 +13,7 @@  #include <net/dsfield.h>  #include <linux/if_vlan.h>  #include <linux/mpls.h> +#include <linux/gcd.h>  #include "core.h"  #include "rdev-ops.h" @@ -1158,7 +1159,8 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)  		   58500000,  		   65000000,  		   78000000, -		   0, +		/* not in the spec, but some devices use this: */ +		   86500000,  		},  		{  13500000,  		   27000000, @@ -1377,6 +1379,25 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)  	return false;  } +static size_t skip_ie(const u8 *ies, size_t ielen, size_t pos) +{ +	/* we assume a validly formed IEs buffer */ +	u8 len = ies[pos + 1]; + +	pos += 2 + len; + +	/* the IE itself must have 255 bytes for fragments to follow */ +	if (len < 255) +		return pos; + +	while (pos < ielen && ies[pos] == WLAN_EID_FRAGMENT) { +		len = ies[pos + 1]; +		pos += 2 + len; +	} + +	return pos; +} +  size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,  			      const u8 *ids, int n_ids,  			      const u8 *after_ric, int n_after_ric, @@ -1386,14 +1407,14 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,  	while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {  		if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { -			pos += 2 + ies[pos + 1]; +			pos = skip_ie(ies, ielen, pos);  			while (pos < ielen &&  			       !ieee80211_id_in_list(after_ric, n_after_ric,  						     ies[pos])) -				pos += 2 + ies[pos + 1]; +				pos = skip_ie(ies, ielen, pos);  		} else { -			pos += 2 + ies[pos + 1]; +			pos = skip_ie(ies, ielen, pos);  		}  	} @@ -1554,31 +1575,57 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,  }  EXPORT_SYMBOL(ieee80211_chandef_to_operating_class); -int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, -				 u32 beacon_int) +static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int, +				       u32 *beacon_int_gcd, +				       bool *beacon_int_different)  {  	struct wireless_dev *wdev; -	int res = 0; -	if (beacon_int < 10 || beacon_int > 10000) -		return -EINVAL; +	*beacon_int_gcd = 0; +	*beacon_int_different = false; -	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { +	list_for_each_entry(wdev, &wiphy->wdev_list, list) {  		if (!wdev->beacon_interval)  			continue; -		if (wdev->beacon_interval != beacon_int) { -			res = -EINVAL; -			break; + +		if (!*beacon_int_gcd) { +			*beacon_int_gcd = wdev->beacon_interval; +			continue;  		} + +		if (wdev->beacon_interval == *beacon_int_gcd) +			continue; + +		*beacon_int_different = true; +		*beacon_int_gcd = gcd(*beacon_int_gcd, wdev->beacon_interval);  	} -	return res; +	if (new_beacon_int && *beacon_int_gcd != new_beacon_int) { +		if (*beacon_int_gcd) +			*beacon_int_different = true; +		*beacon_int_gcd = gcd(*beacon_int_gcd, new_beacon_int); +	} +} + +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, +				 enum nl80211_iftype iftype, u32 beacon_int) +{ +	/* +	 * This is just a basic pre-condition check; if interface combinations +	 * are possible the driver must already be checking those with a call +	 * to cfg80211_check_combinations(), in which case we'll validate more +	 * through the cfg80211_calculate_bi_data() call and code in +	 * cfg80211_iter_combinations(). +	 */ + +	if (beacon_int < 10 || beacon_int > 10000) +		return -EINVAL; + +	return 0;  }  int cfg80211_iter_combinations(struct wiphy *wiphy, -			       const int num_different_channels, -			       const u8 radar_detect, -			       const int iftype_num[NUM_NL80211_IFTYPES], +			       struct iface_combination_params *params,  			       void (*iter)(const struct ieee80211_iface_combination *c,  					    void *data),  			       void *data) @@ -1588,8 +1635,23 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,  	int i, j, iftype;  	int num_interfaces = 0;  	u32 used_iftypes = 0; +	u32 beacon_int_gcd; +	bool beacon_int_different; + +	/* +	 * This is a bit strange, since the iteration used to rely only on +	 * the data given by the driver, but here it now relies on context, +	 * in form of the currently operating interfaces. +	 * This is OK for all current users, and saves us from having to +	 * push the GCD calculations into all the drivers. +	 * In the future, this should probably rely more on data that's in +	 * cfg80211 already - the only thing not would appear to be any new +	 * interfaces (while being brought up) and channel/radar data. +	 */ +	cfg80211_calculate_bi_data(wiphy, params->new_beacon_int, +				   &beacon_int_gcd, &beacon_int_different); -	if (radar_detect) { +	if (params->radar_detect) {  		rcu_read_lock();  		regdom = rcu_dereference(cfg80211_regdomain);  		if (regdom) @@ -1598,8 +1660,8 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,  	}  	for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { -		num_interfaces += iftype_num[iftype]; -		if (iftype_num[iftype] > 0 && +		num_interfaces += params->iftype_num[iftype]; +		if (params->iftype_num[iftype] > 0 &&  		    !(wiphy->software_iftypes & BIT(iftype)))  			used_iftypes |= BIT(iftype);  	} @@ -1613,7 +1675,7 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,  		if (num_interfaces > c->max_interfaces)  			continue; -		if (num_different_channels > c->num_different_channels) +		if (params->num_different_channels > c->num_different_channels)  			continue;  		limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, @@ -1628,16 +1690,17 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,  				all_iftypes |= limits[j].types;  				if (!(limits[j].types & BIT(iftype)))  					continue; -				if (limits[j].max < iftype_num[iftype]) +				if (limits[j].max < params->iftype_num[iftype])  					goto cont; -				limits[j].max -= iftype_num[iftype]; +				limits[j].max -= params->iftype_num[iftype];  			}  		} -		if (radar_detect != (c->radar_detect_widths & radar_detect)) +		if (params->radar_detect != +			(c->radar_detect_widths & params->radar_detect))  			goto cont; -		if (radar_detect && c->radar_detect_regions && +		if (params->radar_detect && c->radar_detect_regions &&  		    !(c->radar_detect_regions & BIT(region)))  			goto cont; @@ -1649,6 +1712,14 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,  		if ((all_iftypes & used_iftypes) != used_iftypes)  			goto cont; +		if (beacon_int_gcd) { +			if (c->beacon_int_min_gcd && +			    beacon_int_gcd < c->beacon_int_min_gcd) +				goto cont; +			if (!c->beacon_int_min_gcd && beacon_int_different) +				goto cont; +		} +  		/* This combination covered all interface types and  		 * supported the requested numbers, so we're good.  		 */ @@ -1671,14 +1742,11 @@ cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c,  }  int cfg80211_check_combinations(struct wiphy *wiphy, -				const int num_different_channels, -				const u8 radar_detect, -				const int iftype_num[NUM_NL80211_IFTYPES]) +				struct iface_combination_params *params)  {  	int err, num = 0; -	err = cfg80211_iter_combinations(wiphy, num_different_channels, -					 radar_detect, iftype_num, +	err = cfg80211_iter_combinations(wiphy, params,  					 cfg80211_iter_sum_ifcombs, &num);  	if (err)  		return err;  |