diff options
Diffstat (limited to 'net/wireless/util.c')
| -rw-r--r-- | net/wireless/util.c | 126 | 
1 files changed, 118 insertions, 8 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c index 6a750bc6bcfe..f0536d44d43c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -544,7 +544,8 @@ EXPORT_SYMBOL(ieee80211_data_from_8023);  void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,  			      const u8 *addr, enum nl80211_iftype iftype, -			      const unsigned int extra_headroom) +			      const unsigned int extra_headroom, +			      bool has_80211_header)  {  	struct sk_buff *frame = NULL;  	u16 ethertype; @@ -553,14 +554,18 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,  	int remaining, err;  	u8 dst[ETH_ALEN], src[ETH_ALEN]; -	err = ieee80211_data_to_8023(skb, addr, iftype); -	if (err) -		goto out; +	if (has_80211_header) { +		err = ieee80211_data_to_8023(skb, addr, iftype); +		if (err) +			goto out; -	/* skip the wrapping header */ -	eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); -	if (!eth) -		goto out; +		/* skip the wrapping header */ +		eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); +		if (!eth) +			goto out; +	} else { +		eth = (struct ethhdr *) skb->data; +	}  	while (skb != frame) {  		u8 padding; @@ -803,6 +808,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,  		return -EBUSY;  	if (ntype != otype) { +		err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, +						    ntype); +		if (err) +			return err; +  		dev->ieee80211_ptr->use_4addr = false;  		dev->ieee80211_ptr->mesh_id_up_len = 0; @@ -896,3 +906,103 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate)  	/* do NOT round down here */  	return (bitrate + 50000) / 100000;  } + +int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, +				 u32 beacon_int) +{ +	struct wireless_dev *wdev; +	int res = 0; + +	if (!beacon_int) +		return -EINVAL; + +	mutex_lock(&rdev->devlist_mtx); + +	list_for_each_entry(wdev, &rdev->netdev_list, list) { +		if (!wdev->beacon_interval) +			continue; +		if (wdev->beacon_interval != beacon_int) { +			res = -EINVAL; +			break; +		} +	} + +	mutex_unlock(&rdev->devlist_mtx); + +	return res; +} + +int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, +				  struct wireless_dev *wdev, +				  enum nl80211_iftype iftype) +{ +	struct wireless_dev *wdev_iter; +	int num[NUM_NL80211_IFTYPES]; +	int total = 1; +	int i, j; + +	ASSERT_RTNL(); + +	/* Always allow software iftypes */ +	if (rdev->wiphy.software_iftypes & BIT(iftype)) +		return 0; + +	/* +	 * Drivers will gradually all set this flag, until all +	 * have it we only enforce for those that set it. +	 */ +	if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) +		return 0; + +	memset(num, 0, sizeof(num)); + +	num[iftype] = 1; + +	mutex_lock(&rdev->devlist_mtx); +	list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { +		if (wdev_iter == wdev) +			continue; +		if (!netif_running(wdev_iter->netdev)) +			continue; + +		if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) +			continue; + +		num[wdev_iter->iftype]++; +		total++; +	} +	mutex_unlock(&rdev->devlist_mtx); + +	for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { +		const struct ieee80211_iface_combination *c; +		struct ieee80211_iface_limit *limits; + +		c = &rdev->wiphy.iface_combinations[i]; + +		limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, +				 GFP_KERNEL); +		if (!limits) +			return -ENOMEM; +		if (total > c->max_interfaces) +			goto cont; + +		for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { +			if (rdev->wiphy.software_iftypes & BIT(iftype)) +				continue; +			for (j = 0; j < c->n_limits; j++) { +				if (!(limits[j].types & iftype)) +					continue; +				if (limits[j].max < num[iftype]) +					goto cont; +				limits[j].max -= num[iftype]; +			} +		} +		/* yay, it fits */ +		kfree(limits); +		return 0; + cont: +		kfree(limits); +	} + +	return -EBUSY; +}  |