diff options
Diffstat (limited to 'net/mac80211')
| -rw-r--r-- | net/mac80211/cfg.c | 3 | ||||
| -rw-r--r-- | net/mac80211/ibss.c | 10 | ||||
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
| -rw-r--r-- | net/mac80211/main.c | 6 | ||||
| -rw-r--r-- | net/mac80211/offchannel.c | 2 | ||||
| -rw-r--r-- | net/mac80211/rx.c | 72 | ||||
| -rw-r--r-- | net/mac80211/scan.c | 2 | ||||
| -rw-r--r-- | net/mac80211/sta_info.c | 11 | ||||
| -rw-r--r-- | net/mac80211/status.c | 9 | ||||
| -rw-r--r-- | net/mac80211/tx.c | 9 | ||||
| -rw-r--r-- | net/mac80211/util.c | 44 | 
11 files changed, 130 insertions, 40 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 05f3a313db8..7371f676cf4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2594,6 +2594,9 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,  		else  			local->probe_req_reg--; +		if (!local->open_count) +			break; +  		ieee80211_queue_work(&local->hw, &local->reconfig_filter);  		break;  	default: diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 5f3620f0bc0..c21e33d1abd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1108,7 +1108,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  	sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;  	sdata->u.ibss.ibss_join_req = jiffies; -	memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); +	memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);  	sdata->u.ibss.ssid_len = params->ssid_len;  	mutex_unlock(&sdata->u.ibss.mtx); @@ -1151,10 +1151,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  	mutex_lock(&sdata->u.ibss.mtx); -	sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; -	memset(sdata->u.ibss.bssid, 0, ETH_ALEN); -	sdata->u.ibss.ssid_len = 0; -  	active_ibss = ieee80211_sta_active_ibss(sdata);  	if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { @@ -1175,6 +1171,10 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  		}  	} +	ifibss->state = IEEE80211_IBSS_MLME_SEARCH; +	memset(ifibss->bssid, 0, ETH_ALEN); +	ifibss->ssid_len = 0; +  	sta_info_flush(sdata->local, sdata);  	spin_lock_bh(&ifibss->incomplete_lock); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8c804550465..156e5835e37 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1314,6 +1314,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  					 struct net_device *dev);  netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  				       struct net_device *dev); +void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, +			      struct sk_buff_head *skbs);  /* HT */  void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c80c4490351..f57f597972f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -871,8 +871,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  				local->hw.wiphy->cipher_suites,  				sizeof(u32) * local->hw.wiphy->n_cipher_suites,  				GFP_KERNEL); -			if (!suites) -				return -ENOMEM; +			if (!suites) { +				result = -ENOMEM; +				goto fail_wiphy_register; +			}  			for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {  				u32 suite = local->hw.wiphy->cipher_suites[r];  				if (suite == WLAN_CIPHER_SUITE_WEP40 || diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 83608ac1678..2c84185dfdb 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -458,8 +458,6 @@ void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)  		list_move_tail(&roc->list, &tmp_list);  		roc->abort = true;  	} - -	ieee80211_start_next_roc(local);  	mutex_unlock(&local->mtx);  	list_for_each_entry_safe(roc, tmp, &tmp_list, list) { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 61c621e9273..00ade7feb2e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -531,6 +531,11 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)  		if (ieee80211_is_action(hdr->frame_control)) {  			u8 category; + +			/* make sure category field is present */ +			if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) +				return RX_DROP_MONITOR; +  			mgmt = (struct ieee80211_mgmt *)hdr;  			category = mgmt->u.action.category;  			if (category != WLAN_CATEGORY_MESH_ACTION && @@ -883,14 +888,16 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)  		 */  		if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION &&  		    ieee80211_is_data_present(hdr->frame_control)) { -			u16 ethertype; -			u8 *payload; +			unsigned int hdrlen; +			__be16 ethertype; -			payload = rx->skb->data + -				ieee80211_hdrlen(hdr->frame_control); -			ethertype = (payload[6] << 8) | payload[7]; -			if (cpu_to_be16(ethertype) == -			    rx->sdata->control_port_protocol) +			hdrlen = ieee80211_hdrlen(hdr->frame_control); + +			if (rx->skb->len < hdrlen + 8) +				return RX_DROP_MONITOR; + +			skb_copy_bits(rx->skb, hdrlen + 6, ðertype, 2); +			if (ethertype == rx->sdata->control_port_protocol)  				return RX_CONTINUE;  		} @@ -1462,11 +1469,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	hdr = (struct ieee80211_hdr *)rx->skb->data;  	fc = hdr->frame_control; + +	if (ieee80211_is_ctl(fc)) +		return RX_CONTINUE; +  	sc = le16_to_cpu(hdr->seq_ctrl);  	frag = sc & IEEE80211_SCTL_FRAG;  	if (likely((!ieee80211_has_morefrags(fc) && frag == 0) || -		   (rx->skb)->len < 24 ||  		   is_multicast_ether_addr(hdr->addr1))) {  		/* not fragmented */  		goto out; @@ -1889,6 +1899,20 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	hdr = (struct ieee80211_hdr *) skb->data;  	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	/* make sure fixed part of mesh header is there, also checks skb len */ +	if (!pskb_may_pull(rx->skb, hdrlen + 6)) +		return RX_DROP_MONITOR; + +	mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + +	/* make sure full mesh header is there, also checks skb len */ +	if (!pskb_may_pull(rx->skb, +			   hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr))) +		return RX_DROP_MONITOR; + +	/* reload pointers */ +	hdr = (struct ieee80211_hdr *) skb->data;  	mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);  	/* frame is in RMC, don't forward */ @@ -1897,7 +1921,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	    mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata))  		return RX_DROP_MONITOR; -	if (!ieee80211_is_data(hdr->frame_control)) +	if (!ieee80211_is_data(hdr->frame_control) || +	    !(status->rx_flags & IEEE80211_RX_RA_MATCH))  		return RX_CONTINUE;  	if (!mesh_hdr->ttl) @@ -1911,9 +1936,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  		if (is_multicast_ether_addr(hdr->addr1)) {  			mpp_addr = hdr->addr3;  			proxied_addr = mesh_hdr->eaddr1; -		} else { +		} else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) { +			/* has_a4 already checked in ieee80211_rx_mesh_check */  			mpp_addr = hdr->addr4;  			proxied_addr = mesh_hdr->eaddr2; +		} else { +			return RX_DROP_MONITOR;  		}  		rcu_read_lock(); @@ -1941,12 +1969,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	}  	skb_set_queue_mapping(skb, q); -	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) -		goto out; -  	if (!--mesh_hdr->ttl) {  		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); -		return RX_DROP_MONITOR; +		goto out;  	}  	if (!ifmsh->mshcfg.dot11MeshForwarding) @@ -2353,6 +2378,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		}  		break;  	case WLAN_CATEGORY_SELF_PROTECTED: +		if (len < (IEEE80211_MIN_ACTION_SIZE + +			   sizeof(mgmt->u.action.u.self_prot.action_code))) +			break; +  		switch (mgmt->u.action.u.self_prot.action_code) {  		case WLAN_SP_MESH_PEERING_OPEN:  		case WLAN_SP_MESH_PEERING_CLOSE: @@ -2371,6 +2400,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		}  		break;  	case WLAN_CATEGORY_MESH_ACTION: +		if (len < (IEEE80211_MIN_ACTION_SIZE + +			   sizeof(mgmt->u.action.u.mesh_action.action_code))) +			break; +  		if (!ieee80211_vif_is_mesh(&sdata->vif))  			break;  		if (mesh_action_is_path_sel(mgmt) && @@ -2913,10 +2946,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,  	if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))  		local->dot11ReceivedFragmentCount++; -	if (ieee80211_is_mgmt(fc)) -		err = skb_linearize(skb); -	else +	if (ieee80211_is_mgmt(fc)) { +		/* drop frame if too short for header */ +		if (skb->len < ieee80211_hdrlen(fc)) +			err = -ENOBUFS; +		else +			err = skb_linearize(skb); +	} else {  		err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); +	}  	if (err) {  		dev_kfree_skb(skb); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index c4cdbde24fd..43e60b5a754 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -917,7 +917,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,  				       struct cfg80211_sched_scan_request *req)  {  	struct ieee80211_local *local = sdata->local; -	struct ieee80211_sched_scan_ies sched_scan_ies; +	struct ieee80211_sched_scan_ies sched_scan_ies = {};  	int ret, i;  	mutex_lock(&local->mtx); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 0a4e4c04db8..d2eb64e1235 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -117,8 +117,8 @@ static void free_sta_work(struct work_struct *wk)  	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {  		local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); -		__skb_queue_purge(&sta->ps_tx_buf[ac]); -		__skb_queue_purge(&sta->tx_filtered[ac]); +		ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); +		ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);  	}  #ifdef CONFIG_MAC80211_MESH @@ -141,7 +141,7 @@ static void free_sta_work(struct work_struct *wk)  		tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);  		if (!tid_tx)  			continue; -		__skb_queue_purge(&tid_tx->pending); +		ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);  		kfree(tid_tx);  	} @@ -961,6 +961,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  	struct ieee80211_local *local = sdata->local;  	struct sk_buff_head pending;  	int filtered = 0, buffered = 0, ac; +	unsigned long flags;  	clear_sta_flag(sta, WLAN_STA_SP); @@ -976,12 +977,16 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {  		int count = skb_queue_len(&pending), tmp; +		spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);  		skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending); +		spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);  		tmp = skb_queue_len(&pending);  		filtered += tmp - count;  		count = tmp; +		spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);  		skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending); +		spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);  		tmp = skb_queue_len(&pending);  		buffered += tmp - count;  	} diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3af0cc4130f..101eb88a2b7 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -668,3 +668,12 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)  	dev_kfree_skb_any(skb);  }  EXPORT_SYMBOL(ieee80211_free_txskb); + +void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, +			      struct sk_buff_head *skbs) +{ +	struct sk_buff *skb; + +	while ((skb = __skb_dequeue(skbs))) +		ieee80211_free_txskb(hw, skb); +} diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c9bf83f3665..b858ebe41fd 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1358,7 +1358,7 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  		if (tx->skb)  			ieee80211_free_txskb(&tx->local->hw, tx->skb);  		else -			__skb_queue_purge(&tx->skbs); +			ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);  		return -1;  	} else if (unlikely(res == TX_QUEUED)) {  		I802_DEBUG_INC(tx->local->tx_handlers_queued); @@ -2120,10 +2120,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,   */  void ieee80211_clear_tx_pending(struct ieee80211_local *local)  { +	struct sk_buff *skb;  	int i; -	for (i = 0; i < local->hw.queues; i++) -		skb_queue_purge(&local->pending[i]); +	for (i = 0; i < local->hw.queues; i++) { +		while ((skb = skb_dequeue(&local->pending[i])) != NULL) +			ieee80211_free_txskb(&local->hw, skb); +	}  }  /* diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 94e58687397..0151ae33c4c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -643,13 +643,41 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			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; +		switch (id) { +		case WLAN_EID_SSID: +		case WLAN_EID_SUPP_RATES: +		case WLAN_EID_FH_PARAMS: +		case WLAN_EID_DS_PARAMS: +		case WLAN_EID_CF_PARAMS: +		case WLAN_EID_TIM: +		case WLAN_EID_IBSS_PARAMS: +		case WLAN_EID_CHALLENGE: +		case WLAN_EID_RSN: +		case WLAN_EID_ERP_INFO: +		case WLAN_EID_EXT_SUPP_RATES: +		case WLAN_EID_HT_CAPABILITY: +		case WLAN_EID_HT_OPERATION: +		case WLAN_EID_VHT_CAPABILITY: +		case WLAN_EID_VHT_OPERATION: +		case WLAN_EID_MESH_ID: +		case WLAN_EID_MESH_CONFIG: +		case WLAN_EID_PEER_MGMT: +		case WLAN_EID_PREQ: +		case WLAN_EID_PREP: +		case WLAN_EID_PERR: +		case WLAN_EID_RANN: +		case WLAN_EID_CHANNEL_SWITCH: +		case WLAN_EID_EXT_CHANSWITCH_ANN: +		case WLAN_EID_COUNTRY: +		case WLAN_EID_PWR_CONSTRAINT: +		case WLAN_EID_TIMEOUT_INTERVAL: +			if (test_bit(id, seen_elems)) { +				elems->parse_error = true; +				left -= elen; +				pos += elen; +				continue; +			} +			break;  		}  		if (calc_crc && id < 64 && (filter & (1ULL << id))) @@ -1463,6 +1491,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)  		list_for_each_entry(sdata, &local->interfaces, list) {  			if (sdata->vif.type != NL80211_IFTYPE_STATION)  				continue; +			if (!sdata->u.mgd.associated) +				continue;  			ieee80211_send_nullfunc(local, sdata, 0);  		}  |