diff options
Diffstat (limited to 'net/mac80211/rx.c')
| -rw-r--r-- | net/mac80211/rx.c | 273 | 
1 files changed, 175 insertions, 98 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fb123e2e081..f407427c642 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -28,6 +28,7 @@  #include "wpa.h"  #include "tkip.h"  #include "wme.h" +#include "rate.h"  /*   * monitor mode reception @@ -748,10 +749,11 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)  	struct ieee80211_local *local = rx->local;  	struct ieee80211_hw *hw = &local->hw;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct sta_info *sta = rx->sta;  	struct tid_ampdu_rx *tid_agg_rx;  	u16 sc; -	int tid; +	u8 tid, ack_policy;  	if (!ieee80211_is_data_qos(hdr->frame_control))  		goto dont_reorder; @@ -764,6 +766,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)  	if (!sta)  		goto dont_reorder; +	ack_policy = *ieee80211_get_qos_ctl(hdr) & +		     IEEE80211_QOS_CTL_ACK_POLICY_MASK;  	tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;  	tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); @@ -774,6 +778,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)  	if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))  		goto dont_reorder; +	/* not part of a BA session */ +	if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && +	    ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) +		goto dont_reorder; + +	/* not actually part of this BA session */ +	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) +		goto dont_reorder; +  	/* new, potentially un-ordered, ampdu frame - process it */  	/* reset session timer */ @@ -858,6 +871,13 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)  			    rx->sdata->control_port_protocol)  				return RX_CONTINUE;  		} + +		if (rx->sdata->vif.type == NL80211_IFTYPE_AP && +		    cfg80211_rx_spurious_frame(rx->sdata->dev, +					       hdr->addr2, +					       GFP_ATOMIC)) +			return RX_DROP_UNUSABLE; +  		return RX_DROP_MONITOR;  	} @@ -1327,15 +1347,20 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)  		/*  		 * If we receive a 4-addr nullfunc frame from a STA -		 * that was not moved to a 4-addr STA vlan yet, drop -		 * the frame to the monitor interface, to make sure -		 * that hostapd sees it +		 * that was not moved to a 4-addr STA vlan yet send +		 * the event to userspace and for older hostapd drop +		 * the frame to the monitor interface.  		 */  		if (ieee80211_has_a4(hdr->frame_control) &&  		    (rx->sdata->vif.type == NL80211_IFTYPE_AP ||  		     (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && -		      !rx->sdata->u.vlan.sta))) +		      !rx->sdata->u.vlan.sta))) { +			if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) +				cfg80211_rx_unexpected_4addr_frame( +					rx->sdata->dev, sta->sta.addr, +					GFP_ATOMIC);  			return RX_DROP_MONITOR; +		}  		/*  		 * Update counter and free packet here to avoid  		 * counting this as a dropped packed. @@ -1551,25 +1576,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	return RX_CONTINUE;  } -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) -{ -	u8 *data = rx->skb->data; -	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; - -	if (!ieee80211_is_data_qos(hdr->frame_control)) -		return RX_CONTINUE; - -	/* remove the qos control field, update frame type and meta-data */ -	memmove(data + IEEE80211_QOS_CTL_LEN, data, -		ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN); -	hdr = (struct ieee80211_hdr *)skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN); -	/* change frame type to non QOS */ -	hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA); - -	return RX_CONTINUE; -} -  static int  ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)  { @@ -1802,7 +1808,12 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  	}  	if (xmit_skb) { -		/* send to wireless media */ +		/* +		 * Send to wireless media and increase priority by 256 to +		 * keep the received priority instead of reclassifying +		 * the frame (see cfg80211_classify8021d). +		 */ +		xmit_skb->priority += 256;  		xmit_skb->protocol = htons(ETH_P_802_3);  		skb_reset_network_header(xmit_skb);  		skb_reset_mac_header(xmit_skb); @@ -1871,13 +1882,16 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)  static ieee80211_rx_result  ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  { -	struct ieee80211_hdr *hdr; +	struct ieee80211_hdr *fwd_hdr, *hdr; +	struct ieee80211_tx_info *info;  	struct ieee80211s_hdr *mesh_hdr; -	unsigned int hdrlen;  	struct sk_buff *skb = rx->skb, *fwd_skb;  	struct ieee80211_local *local = rx->local;  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	__le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); +	u16 q, hdrlen;  	hdr = (struct ieee80211_hdr *) skb->data;  	hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -1893,15 +1907,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  		return RX_CONTINUE;  	if (!mesh_hdr->ttl) -		/* illegal frame */  		return RX_DROP_MONITOR; -	if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) { -		IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -						dropped_frames_congestion); -		return RX_DROP_MONITOR; -	} -  	if (mesh_hdr->flags & MESH_FLAGS_AE) {  		struct mesh_path *mppath;  		char *proxied_addr; @@ -1933,60 +1940,50 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	    compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0)  		return RX_CONTINUE; -	mesh_hdr->ttl--; - -	if (status->rx_flags & IEEE80211_RX_RA_MATCH) { -		if (!mesh_hdr->ttl) -			IEEE80211_IFSTA_MESH_CTR_INC(&rx->sdata->u.mesh, -						     dropped_frames_ttl); -		else { -			struct ieee80211_hdr *fwd_hdr; -			struct ieee80211_tx_info *info; +	q = ieee80211_select_queue_80211(local, skb, hdr); +	if (ieee80211_queue_stopped(&local->hw, q)) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); +		return RX_DROP_MONITOR; +	} +	skb_set_queue_mapping(skb, q); -			fwd_skb = skb_copy(skb, GFP_ATOMIC); +	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) +		goto out; -			if (!fwd_skb && net_ratelimit()) -				printk(KERN_DEBUG "%s: failed to clone mesh frame\n", -						   sdata->name); -			if (!fwd_skb) -				goto out; +	if (!--mesh_hdr->ttl) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); +		return RX_DROP_MONITOR; +	} -			fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data; -			memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); -			info = IEEE80211_SKB_CB(fwd_skb); -			memset(info, 0, sizeof(*info)); -			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; -			info->control.vif = &rx->sdata->vif; -			if (is_multicast_ether_addr(fwd_hdr->addr1)) { -				IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -								fwded_mcast); -				skb_set_queue_mapping(fwd_skb, -					ieee80211_select_queue(sdata, fwd_skb)); -				ieee80211_set_qos_hdr(sdata, fwd_skb); -			} else { -				int err; -				/* -				 * Save TA to addr1 to send TA a path error if a -				 * suitable next hop is not found -				 */ -				memcpy(fwd_hdr->addr1, fwd_hdr->addr2, -						ETH_ALEN); -				err = mesh_nexthop_lookup(fwd_skb, sdata); -				/* Failed to immediately resolve next hop: -				 * fwded frame was dropped or will be added -				 * later to the pending skb queue.  */ -				if (err) -					return RX_DROP_MONITOR; +	fwd_skb = skb_copy(skb, GFP_ATOMIC); +	if (!fwd_skb) { +		if (net_ratelimit()) +			printk(KERN_DEBUG "%s: failed to clone mesh frame\n", +					sdata->name); +		goto out; +	} -				IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -								fwded_unicast); -			} -			IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -						     fwded_frames); -			ieee80211_add_pending_skb(local, fwd_skb); -		} +	fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data; +	info = IEEE80211_SKB_CB(fwd_skb); +	memset(info, 0, sizeof(*info)); +	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +	info->control.vif = &rx->sdata->vif; +	info->control.jiffies = jiffies; +	if (is_multicast_ether_addr(fwd_hdr->addr1)) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); +		memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); +	} else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); +	} else { +		/* unable to resolve next hop */ +		mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, +				    0, reason, fwd_hdr->addr2, sdata); +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); +		return RX_DROP_MONITOR;  	} +	IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); +	ieee80211_add_pending_skb(local, fwd_skb);   out:  	if (is_multicast_ether_addr(hdr->addr1) ||  	    sdata->dev->flags & IFF_PROMISC) @@ -2014,12 +2011,17 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)  		return RX_DROP_MONITOR;  	/* -	 * Allow the cooked monitor interface of an AP to see 4-addr frames so -	 * that a 4-addr station can be detected and moved into a separate VLAN +	 * Send unexpected-4addr-frame event to hostapd. For older versions, +	 * also drop the frame to cooked monitor interfaces.  	 */  	if (ieee80211_has_a4(hdr->frame_control) && -	    sdata->vif.type == NL80211_IFTYPE_AP) +	    sdata->vif.type == NL80211_IFTYPE_AP) { +		if (rx->sta && +		    !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) +			cfg80211_rx_unexpected_4addr_frame( +				rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC);  		return RX_DROP_MONITOR; +	}  	err = __ieee80211_data_to_8023(rx, &port_control);  	if (unlikely(err)) @@ -2174,6 +2176,18 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)  	if (!ieee80211_is_mgmt(mgmt->frame_control))  		return RX_DROP_MONITOR; +	if (rx->sdata->vif.type == NL80211_IFTYPE_AP && +	    ieee80211_is_beacon(mgmt->frame_control) && +	    !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { +		struct ieee80211_rx_status *status; + +		status = IEEE80211_SKB_RXCB(rx->skb); +		cfg80211_report_obss_beacon(rx->local->hw.wiphy, +					    rx->skb->data, rx->skb->len, +					    status->freq, GFP_ATOMIC); +		rx->flags |= IEEE80211_RX_BEACON_REPORTED; +	} +  	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))  		return RX_DROP_MONITOR; @@ -2206,16 +2220,69 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		return RX_DROP_UNUSABLE;  	switch (mgmt->u.action.category) { +	case WLAN_CATEGORY_HT: +		/* reject HT action frames from stations not supporting HT */ +		if (!rx->sta->sta.ht_cap.ht_supported) +			goto invalid; + +		if (sdata->vif.type != NL80211_IFTYPE_STATION && +		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT && +		    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && +		    sdata->vif.type != NL80211_IFTYPE_AP && +		    sdata->vif.type != NL80211_IFTYPE_ADHOC) +			break; + +		/* verify action & smps_control are present */ +		if (len < IEEE80211_MIN_ACTION_SIZE + 2) +			goto invalid; + +		switch (mgmt->u.action.u.ht_smps.action) { +		case WLAN_HT_ACTION_SMPS: { +			struct ieee80211_supported_band *sband; +			u8 smps; + +			/* convert to HT capability */ +			switch (mgmt->u.action.u.ht_smps.smps_control) { +			case WLAN_HT_SMPS_CONTROL_DISABLED: +				smps = WLAN_HT_CAP_SM_PS_DISABLED; +				break; +			case WLAN_HT_SMPS_CONTROL_STATIC: +				smps = WLAN_HT_CAP_SM_PS_STATIC; +				break; +			case WLAN_HT_SMPS_CONTROL_DYNAMIC: +				smps = WLAN_HT_CAP_SM_PS_DYNAMIC; +				break; +			default: +				goto invalid; +			} +			smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + +			/* if no change do nothing */ +			if ((rx->sta->sta.ht_cap.cap & +					IEEE80211_HT_CAP_SM_PS) == smps) +				goto handled; + +			rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS; +			rx->sta->sta.ht_cap.cap |= smps; + +			sband = rx->local->hw.wiphy->bands[status->band]; + +			rate_control_rate_update(local, sband, rx->sta, +						 IEEE80211_RC_SMPS_CHANGED, +						 local->_oper_channel_type); +			goto handled; +		} +		default: +			goto invalid; +		} + +		break;  	case WLAN_CATEGORY_BACK: -		/* -		 * The aggregation code is not prepared to handle -		 * anything but STA/AP due to the BSSID handling; -		 * IBSS could work in the code but isn't supported -		 * by drivers or the standard. -		 */  		if (sdata->vif.type != NL80211_IFTYPE_STATION && +		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&  		    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && -		    sdata->vif.type != NL80211_IFTYPE_AP) +		    sdata->vif.type != NL80211_IFTYPE_AP && +		    sdata->vif.type != NL80211_IFTYPE_ADHOC)  			break;  		/* verify action_code is present */ @@ -2493,6 +2560,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,  		goto out_free_skb;  	rx->flags |= IEEE80211_RX_CMNTR; +	/* If there are no cooked monitor interfaces, just free the SKB */ +	if (!local->cooked_mntrs) +		goto out_free_skb; +  	if (skb_headroom(skb) < sizeof(*rthdr) &&  	    pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC))  		goto out_free_skb; @@ -2628,7 +2699,6 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)  		if (ieee80211_vif_is_mesh(&rx->sdata->vif))  			CALL_RXH(ieee80211_rx_h_mesh_fwding);  #endif -		CALL_RXH(ieee80211_rx_h_remove_qos_control)  		CALL_RXH(ieee80211_rx_h_amsdu)  		CALL_RXH(ieee80211_rx_h_data)  		CALL_RXH(ieee80211_rx_h_ctrl); @@ -2748,8 +2818,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  				rate_idx = 0; /* TODO: HT rates */  			else  				rate_idx = status->rate_idx; -			rx->sta = ieee80211_ibss_add_sta(sdata, bssid, -					hdr->addr2, BIT(rate_idx), GFP_ATOMIC); +			ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, +						 BIT(rate_idx));  		}  		break;  	case NL80211_IFTYPE_MESH_POINT: @@ -2770,10 +2840,17 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  				return 0;  		} else if (!ieee80211_bssid_match(bssid,  					sdata->vif.addr)) { +			/* +			 * Accept public action frames even when the +			 * BSSID doesn't match, this is used for P2P +			 * and location updates. Note that mac80211 +			 * itself never looks at these frames. +			 */ +			if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && +			    ieee80211_is_public_action(hdr, skb->len)) +				return 1;  			if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && -			    !ieee80211_is_beacon(hdr->frame_control) && -			    !(ieee80211_is_action(hdr->frame_control) && -			      sdata->vif.p2p)) +			    !ieee80211_is_beacon(hdr->frame_control))  				return 0;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		}  |