diff options
Diffstat (limited to 'net/mac80211/util.c')
| -rw-r--r-- | net/mac80211/util.c | 80 | 
1 files changed, 65 insertions, 15 deletions
| diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 9919892575f4..32f7a3b3d43c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -572,24 +572,40 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  	size_t left = len;  	u8 *pos = start;  	bool calc_crc = filter != 0; +	DECLARE_BITMAP(seen_elems, 256); +	bitmap_zero(seen_elems, 256);  	memset(elems, 0, sizeof(*elems));  	elems->ie_start = start;  	elems->total_len = len;  	while (left >= 2) {  		u8 id, elen; +		bool elem_parse_failed;  		id = *pos++;  		elen = *pos++;  		left -= 2; -		if (elen > left) +		if (elen > left) { +			elems->parse_error = true;  			break; +		} + +		if (id != WLAN_EID_VENDOR_SPECIFIC && +		    id != WLAN_EID_QUIET && +		    test_bit(id, seen_elems)) { +			elems->parse_error = true; +			left -= elen; +			pos += elen; +			continue; +		}  		if (calc_crc && id < 64 && (filter & (1ULL << id)))  			crc = crc32_be(crc, pos - 2, elen + 2); +		elem_parse_failed = false; +  		switch (id) {  		case WLAN_EID_SSID:  			elems->ssid = pos; @@ -615,7 +631,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			if (elen >= sizeof(struct ieee80211_tim_ie)) {  				elems->tim = (void *)pos;  				elems->tim_len = elen; -			} +			} else +				elem_parse_failed = true;  			break;  		case WLAN_EID_IBSS_PARAMS:  			elems->ibss_params = pos; @@ -664,10 +681,14 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_HT_CAPABILITY:  			if (elen >= sizeof(struct ieee80211_ht_cap))  				elems->ht_cap_elem = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_HT_INFORMATION:  			if (elen >= sizeof(struct ieee80211_ht_info))  				elems->ht_info_elem = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_MESH_ID:  			elems->mesh_id = pos; @@ -676,6 +697,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_MESH_CONFIG:  			if (elen >= sizeof(struct ieee80211_meshconf_ie))  				elems->mesh_config = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_PEER_MGMT:  			elems->peering = pos; @@ -696,6 +719,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_RANN:  			if (elen >= sizeof(struct ieee80211_rann_ie))  				elems->rann = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_CHANNEL_SWITCH:  			elems->ch_switch_elem = pos; @@ -724,10 +749,18 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			break;  		} +		if (elem_parse_failed) +			elems->parse_error = true; +		else +			set_bit(id, seen_elems); +  		left -= elen;  		pos += elen;  	} +	if (left != 0) +		elems->parse_error = true; +  	return crc;  } @@ -737,7 +770,8 @@ void ieee802_11_parse_elems(u8 *start, size_t len,  	ieee802_11_parse_elems_crc(start, len, elems, 0, 0);  } -void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) +void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, +			       bool bss_notify)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_tx_queue_params qparam; @@ -753,7 +787,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)  	use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) &&  		 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); -	for (queue = 0; queue < local_to_hw(local)->queues; queue++) { +	for (queue = 0; queue < local->hw.queues; queue++) {  		/* Set defaults according to 802.11-2007 Table 7-37 */  		aCWmax = 1023;  		if (use_11b) @@ -807,7 +841,9 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)  	if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {  		sdata->vif.bss_conf.qos =  			sdata->vif.type != NL80211_IFTYPE_STATION; -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); +		if (bss_notify) +			ieee80211_bss_info_change_notify(sdata, +							 BSS_CHANGED_QOS);  	}  } @@ -829,7 +865,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,  	else  		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; -	ieee80211_set_wmm_default(sdata); +	ieee80211_set_wmm_default(sdata, true);  }  u32 ieee80211_mandatory_rates(struct ieee80211_local *local, @@ -862,8 +898,8 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,  			 u16 transaction, u16 auth_alg, -			 u8 *extra, size_t extra_len, const u8 *bssid, -			 const u8 *key, u8 key_len, u8 key_idx) +			 u8 *extra, size_t extra_len, const u8 *da, +			 const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)  {  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb; @@ -881,7 +917,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,  	memset(mgmt, 0, 24 + 6);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_AUTH); -	memcpy(mgmt->da, bssid, ETH_ALEN); +	memcpy(mgmt->da, da, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	memcpy(mgmt->bssid, bssid, ETH_ALEN);  	mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); @@ -1185,13 +1221,12 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	mutex_lock(&local->sta_mtx);  	list_for_each_entry(sta, &local->sta_list, list) {  		if (sta->uploaded) { -			sdata = sta->sdata; -			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -				sdata = container_of(sdata->bss, -					     struct ieee80211_sub_if_data, -					     u.ap); +			enum ieee80211_sta_state state; -			WARN_ON(drv_sta_add(local, sdata, &sta->sta)); +			for (state = IEEE80211_STA_NOTEXIST; +			     state < sta->sta_state - 1; state++) +				WARN_ON(drv_sta_state(local, sta->sdata, sta, +						      state, state + 1));  		}  	}  	mutex_unlock(&local->sta_mtx); @@ -1272,6 +1307,21 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	ieee80211_recalc_ps(local, -1);  	/* +	 * The sta might be in psm against the ap (e.g. because +	 * this was the state before a hw restart), so we +	 * explicitly send a null packet in order to make sure +	 * it'll sync against the ap (and get out of psm). +	 */ +	if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { +		list_for_each_entry(sdata, &local->interfaces, list) { +			if (sdata->vif.type != NL80211_IFTYPE_STATION) +				continue; + +			ieee80211_send_nullfunc(local, sdata, 0); +		} +	} + +	/*  	 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation  	 * sessions can be established after a resume.  	 * | 
