diff options
| author | David S. Miller <davem@davemloft.net> | 2010-06-17 14:19:06 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-06-17 14:19:06 -0700 | 
| commit | bb9c03d8a6893517737b16fdbeb54be3c73b3023 (patch) | |
| tree | 35fa0d1defaaf94641963a49126d7bb475ffa4c6 /net/mac80211/rx.c | |
| parent | 4de57826810fd2cfeb2ab5c7d003ff9116b8f7ee (diff) | |
| parent | abf52f86aa0a49a7377350cafa8f218c4cd227e7 (diff) | |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net/mac80211/rx.c')
| -rw-r--r-- | net/mac80211/rx.c | 137 | 
1 files changed, 87 insertions, 50 deletions
| diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6a15632e7eca..a8aa0f2411a2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -719,16 +719,13 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  	tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; -	spin_lock(&sta->lock); - -	if (!sta->ampdu_mlme.tid_active_rx[tid]) -		goto dont_reorder_unlock; - -	tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; +	tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); +	if (!tid_agg_rx) +		goto dont_reorder;  	/* qos null data frames are excluded */  	if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) -		goto dont_reorder_unlock; +		goto dont_reorder;  	/* new, potentially un-ordered, ampdu frame - process it */ @@ -740,20 +737,22 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  	/* if this mpdu is fragmented - terminate rx aggregation session */  	sc = le16_to_cpu(hdr->seq_ctrl);  	if (sc & IEEE80211_SCTL_FRAG) { -		spin_unlock(&sta->lock); -		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, -					       WLAN_REASON_QSTA_REQUIRE_SETUP); -		dev_kfree_skb(skb); +		skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; +		skb_queue_tail(&rx->sdata->skb_queue, skb); +		ieee80211_queue_work(&local->hw, &rx->sdata->work);  		return;  	} -	if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) { -		spin_unlock(&sta->lock); +	/* +	 * No locking needed -- we will only ever process one +	 * RX packet at a time, and thus own tid_agg_rx. All +	 * other code manipulating it needs to (and does) make +	 * sure that we cannot get to it any more before doing +	 * anything with it. +	 */ +	if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))  		return; -	} - dont_reorder_unlock: -	spin_unlock(&sta->lock);   dont_reorder:  	__skb_queue_tail(frames, skb);  } @@ -1268,11 +1267,13 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  						 rx->queue, &(rx->skb));  		if (rx->key && rx->key->conf.alg == ALG_CCMP &&  		    ieee80211_has_protected(fc)) { +			int queue = ieee80211_is_mgmt(fc) ? +				NUM_RX_DATA_QUEUES : rx->queue;  			/* Store CCMP PN so that we can verify that the next  			 * fragment has a sequential PN value. */  			entry->ccmp = 1;  			memcpy(entry->last_pn, -			       rx->key->u.ccmp.rx_pn[rx->queue], +			       rx->key->u.ccmp.rx_pn[queue],  			       CCMP_PN_LEN);  		}  		return RX_QUEUED; @@ -1292,6 +1293,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	if (entry->ccmp) {  		int i;  		u8 pn[CCMP_PN_LEN], *rpn; +		int queue;  		if (!rx->key || rx->key->conf.alg != ALG_CCMP)  			return RX_DROP_UNUSABLE;  		memcpy(pn, entry->last_pn, CCMP_PN_LEN); @@ -1300,7 +1302,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  			if (pn[i])  				break;  		} -		rpn = rx->key->u.ccmp.rx_pn[rx->queue]; +		queue = ieee80211_is_mgmt(fc) ? +			NUM_RX_DATA_QUEUES : rx->queue; +		rpn = rx->key->u.ccmp.rx_pn[queue];  		if (memcmp(pn, rpn, CCMP_PN_LEN))  			return RX_DROP_UNUSABLE;  		memcpy(entry->last_pn, pn, CCMP_PN_LEN); @@ -1830,13 +1834,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)  				  &bar_data, sizeof(bar_data)))  			return RX_DROP_MONITOR; -		spin_lock(&rx->sta->lock);  		tid = le16_to_cpu(bar_data.control) >> 12; -		if (!rx->sta->ampdu_mlme.tid_active_rx[tid]) { -			spin_unlock(&rx->sta->lock); + +		tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]); +		if (!tid_agg_rx)  			return RX_DROP_MONITOR; -		} -		tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];  		start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4; @@ -1849,7 +1851,6 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)  		ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,  						 frames);  		kfree_skb(skb); -		spin_unlock(&rx->sta->lock);  		return RX_QUEUED;  	} @@ -1950,30 +1951,27 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		if (len < IEEE80211_MIN_ACTION_SIZE + 1)  			break; -		if (sdata->vif.type == NL80211_IFTYPE_STATION) -			return ieee80211_sta_rx_mgmt(sdata, rx->skb); -  		switch (mgmt->u.action.u.addba_req.action_code) {  		case WLAN_ACTION_ADDBA_REQ:  			if (len < (IEEE80211_MIN_ACTION_SIZE +  				   sizeof(mgmt->u.action.u.addba_req))) -				return RX_DROP_MONITOR; -			ieee80211_process_addba_request(local, rx->sta, mgmt, len); -			goto handled; +				goto invalid; +			break;  		case WLAN_ACTION_ADDBA_RESP:  			if (len < (IEEE80211_MIN_ACTION_SIZE +  				   sizeof(mgmt->u.action.u.addba_resp))) -				break; -			ieee80211_process_addba_resp(local, rx->sta, mgmt, len); -			goto handled; +				goto invalid; +			break;  		case WLAN_ACTION_DELBA:  			if (len < (IEEE80211_MIN_ACTION_SIZE +  				   sizeof(mgmt->u.action.u.delba))) -				break; -			ieee80211_process_delba(sdata, rx->sta, mgmt, len); -			goto handled; +				goto invalid; +			break; +		default: +			goto invalid;  		} -		break; + +		goto queue;  	case WLAN_CATEGORY_SPECTRUM_MGMT:  		if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)  			break; @@ -2003,7 +2001,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  			if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))  				break; -			return ieee80211_sta_rx_mgmt(sdata, rx->skb); +			goto queue;  		}  		break;  	case WLAN_CATEGORY_SA_QUERY: @@ -2021,11 +2019,12 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		break;  	case WLAN_CATEGORY_MESH_PLINK:  	case WLAN_CATEGORY_MESH_PATH_SEL: -		if (ieee80211_vif_is_mesh(&sdata->vif)) -			return ieee80211_mesh_rx_mgmt(sdata, rx->skb); -		break; +		if (!ieee80211_vif_is_mesh(&sdata->vif)) +			break; +		goto queue;  	} + invalid:  	/*  	 * For AP mode, hostapd is responsible for handling any action  	 * frames that we didn't handle, including returning unknown @@ -2045,8 +2044,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  	 */  	status = IEEE80211_SKB_RXCB(rx->skb); -	if (sdata->vif.type == NL80211_IFTYPE_STATION && -	    cfg80211_rx_action(rx->sdata->dev, status->freq, +	if (cfg80211_rx_action(rx->sdata->dev, status->freq,  			       rx->skb->data, rx->skb->len,  			       GFP_ATOMIC))  		goto handled; @@ -2074,6 +2072,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		rx->sta->rx_packets++;  	dev_kfree_skb(rx->skb);  	return RX_QUEUED; + + queue: +	rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; +	skb_queue_tail(&sdata->skb_queue, rx->skb); +	ieee80211_queue_work(&local->hw, &sdata->work); +	if (rx->sta) +		rx->sta->rx_packets++; +	return RX_QUEUED;  }  static ieee80211_rx_result debug_noinline @@ -2081,10 +2087,15 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)  {  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	ieee80211_rx_result rxs; +	struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; +	__le16 stype;  	if (!(rx->flags & IEEE80211_RX_RA_MATCH))  		return RX_DROP_MONITOR; +	if (rx->skb->len < 24) +		return RX_DROP_MONITOR; +  	if (ieee80211_drop_unencrypted_mgmt(rx))  		return RX_DROP_UNUSABLE; @@ -2092,16 +2103,42 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)  	if (rxs != RX_CONTINUE)  		return rxs; -	if (ieee80211_vif_is_mesh(&sdata->vif)) -		return ieee80211_mesh_rx_mgmt(sdata, rx->skb); +	stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE); -	if (sdata->vif.type == NL80211_IFTYPE_ADHOC) -		return ieee80211_ibss_rx_mgmt(sdata, rx->skb); +	if (!ieee80211_vif_is_mesh(&sdata->vif) && +	    sdata->vif.type != NL80211_IFTYPE_ADHOC && +	    sdata->vif.type != NL80211_IFTYPE_STATION) +		return RX_DROP_MONITOR; + +	switch (stype) { +	case cpu_to_le16(IEEE80211_STYPE_BEACON): +	case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): +		/* process for all: mesh, mlme, ibss */ +		break; +	case cpu_to_le16(IEEE80211_STYPE_DEAUTH): +	case cpu_to_le16(IEEE80211_STYPE_DISASSOC): +		/* process only for station */ +		if (sdata->vif.type != NL80211_IFTYPE_STATION) +			return RX_DROP_MONITOR; +		break; +	case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): +	case cpu_to_le16(IEEE80211_STYPE_AUTH): +		/* process only for ibss */ +		if (sdata->vif.type != NL80211_IFTYPE_ADHOC) +			return RX_DROP_MONITOR; +		break; +	default: +		return RX_DROP_MONITOR; +	} -	if (sdata->vif.type == NL80211_IFTYPE_STATION) -		return ieee80211_sta_rx_mgmt(sdata, rx->skb); +	/* queue up frame and kick off work to process it */ +	rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; +	skb_queue_tail(&sdata->skb_queue, rx->skb); +	ieee80211_queue_work(&rx->local->hw, &sdata->work); +	if (rx->sta) +		rx->sta->rx_packets++; -	return RX_DROP_MONITOR; +	return RX_QUEUED;  }  static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr, | 
