diff options
Diffstat (limited to 'net/wireless/scan.c')
| -rw-r--r-- | net/wireless/scan.c | 59 | 
1 files changed, 58 insertions, 1 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 2249b1a89d1c..389a52c29bfc 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1731,6 +1731,61 @@ static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known,  	}  } +static void cfg80211_check_stuck_ecsa(struct cfg80211_registered_device *rdev, +				      struct cfg80211_internal_bss *known, +				      const struct cfg80211_bss_ies *old) +{ +	const struct ieee80211_ext_chansw_ie *ecsa; +	const struct element *elem_new, *elem_old; +	const struct cfg80211_bss_ies *new, *bcn; + +	if (known->pub.proberesp_ecsa_stuck) +		return; + +	new = rcu_dereference_protected(known->pub.proberesp_ies, +					lockdep_is_held(&rdev->bss_lock)); +	if (WARN_ON(!new)) +		return; + +	if (new->tsf - old->tsf < USEC_PER_SEC) +		return; + +	elem_old = cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN, +				      old->data, old->len); +	if (!elem_old) +		return; + +	elem_new = cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN, +				      new->data, new->len); +	if (!elem_new) +		return; + +	bcn = rcu_dereference_protected(known->pub.beacon_ies, +					lockdep_is_held(&rdev->bss_lock)); +	if (bcn && +	    cfg80211_find_elem(WLAN_EID_EXT_CHANSWITCH_ANN, +			       bcn->data, bcn->len)) +		return; + +	if (elem_new->datalen != elem_old->datalen) +		return; +	if (elem_new->datalen < sizeof(struct ieee80211_ext_chansw_ie)) +		return; +	if (memcmp(elem_new->data, elem_old->data, elem_new->datalen)) +		return; + +	ecsa = (void *)elem_new->data; + +	if (!ecsa->mode) +		return; + +	if (ecsa->new_ch_num != +	    ieee80211_frequency_to_channel(known->pub.channel->center_freq)) +		return; + +	known->pub.proberesp_ecsa_stuck = 1; +} +  static bool  cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,  			  struct cfg80211_internal_bss *known, @@ -1750,8 +1805,10 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,  		/* Override possible earlier Beacon frame IEs */  		rcu_assign_pointer(known->pub.ies,  				   new->pub.proberesp_ies); -		if (old) +		if (old) { +			cfg80211_check_stuck_ecsa(rdev, known, old);  			kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); +		}  	}  	if (rcu_access_pointer(new->pub.beacon_ies)) {  | 
