Commit cb17ed29 authored by Mathy Vanhoef's avatar Mathy Vanhoef Committed by Johannes Berg
Browse files

mac80211: parse radiotap header when selecting Tx queue



Already parse the radiotap header in ieee80211_monitor_select_queue.
In a subsequent commit this will allow us to add a radiotap flag that
influences the queue on which injected packets will be sent.

This also fixes the incomplete validation of the injected frame in
ieee80211_monitor_select_queue: currently an out of bounds memory
access may occur in in the called function ieee80211_select_queue_80211
if the 802.11 header is too small.

Note that in ieee80211_monitor_start_xmit the radiotap header is parsed
again, which is necessairy because ieee80211_monitor_select_queue is not
always called beforehand.

Signed-off-by: default avatarMathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
Link: https://lore.kernel.org/r/20200723100153.31631-6-Mathy.Vanhoef@kuleuven.be


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 08aca29a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -6238,6 +6238,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
			      struct ieee80211_vif *vif, struct sk_buff *skb,
			      int band, struct ieee80211_sta **sta);

/**
 * Sanity-check and parse the radiotap header of injected frames
 * @skb: packet injected by userspace
 * @dev: the &struct device of this 802.11 device
 */
bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
				 struct net_device *dev);

/**
 * struct ieee80211_noa_data - holds temporary data for tracking P2P NoA state
 *
+11 −4
Original line number Diff line number Diff line
@@ -1183,17 +1183,24 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
	struct ieee80211_hdr *hdr;
	struct ieee80211_radiotap_header *rtap = (void *)skb->data;
	int len_rthdr;

	if (local->hw.queues < IEEE80211_NUM_ACS)
		return 0;

	if (skb->len < 4 ||
	    skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
	/* reset flags and info before parsing radiotap header */
	memset(info, 0, sizeof(*info));

	if (!ieee80211_parse_tx_radiotap(skb, dev))
		return 0; /* doesn't matter, frame will be dropped */

	hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
	len_rthdr = ieee80211_get_radiotap_len(skb->data);
	hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
	if (skb->len < len_rthdr + 2 ||
	    skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
		return 0; /* doesn't matter, frame will be dropped */

	return ieee80211_select_queue_80211(sdata, skb, hdr);
}
+24 −30
Original line number Diff line number Diff line
@@ -2015,9 +2015,10 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
	ieee80211_tx(sdata, sta, skb, false);
}

static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
					struct sk_buff *skb)
bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
				 struct net_device *dev)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct ieee80211_radiotap_iterator iterator;
	struct ieee80211_radiotap_header *rthdr =
		(struct ieee80211_radiotap_header *) skb->data;
@@ -2036,6 +2037,18 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
	u8 vht_mcs = 0, vht_nss = 0;
	int i;

	/* check for not even having the fixed radiotap header part */
	if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
		return false; /* too short to be possibly valid */

	/* is it a header version we can trust to find length from? */
	if (unlikely(rthdr->it_version))
		return false; /* only version 0 is supported */

	/* does the skb contain enough to deliver on the alleged length? */
	if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data)))
		return false; /* skb too short for claimed rt header extent */

	info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
		       IEEE80211_TX_CTL_DONTFRAG;

@@ -2189,13 +2202,6 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
						     local->hw.max_rate_tries);
	}

	/*
	 * remove the radiotap header
	 * iterator->_max_length was sanity-checked against
	 * skb->len by iterator init
	 */
	skb_pull(skb, iterator._max_length);

	return true;
}

@@ -2204,8 +2210,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
	struct ieee80211_chanctx_conf *chanctx_conf;
	struct ieee80211_radiotap_header *prthdr =
		(struct ieee80211_radiotap_header *)skb->data;
	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
	struct ieee80211_hdr *hdr;
	struct ieee80211_sub_if_data *tmp_sdata, *sdata;
@@ -2213,21 +2217,17 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
	u16 len_rthdr;
	int hdrlen;

	/* check for not even having the fixed radiotap header part */
	if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
		goto fail; /* too short to be possibly valid */
	memset(info, 0, sizeof(*info));
	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
		      IEEE80211_TX_CTL_INJECTED;

	/* is it a header version we can trust to find length from? */
	if (unlikely(prthdr->it_version))
		goto fail; /* only version 0 is supported */
	/* Sanity-check and process the injection radiotap header */
	if (!ieee80211_parse_tx_radiotap(skb, dev))
		goto fail;

	/* then there must be a radiotap header with a length we can use */
	/* we now know there is a radiotap header with a length we can use */
	len_rthdr = ieee80211_get_radiotap_len(skb->data);

	/* does the skb contain enough to deliver on the alleged length? */
	if (unlikely(skb->len < len_rthdr))
		goto fail; /* skb too short for claimed rt header extent */

	/*
	 * fix up the pointers accounting for the radiotap
	 * header still being in there.  We are being given
@@ -2273,11 +2273,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
		skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
	}

	memset(info, 0, sizeof(*info));

	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
		      IEEE80211_TX_CTL_INJECTED;

	rcu_read_lock();

	/*
@@ -2343,9 +2338,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,

	info->band = chandef->chan->band;

	/* process and remove the injection radiotap header */
	if (!ieee80211_parse_tx_radiotap(local, skb))
		goto fail_rcu;
	/* remove the injection radiotap header */
	skb_pull(skb, len_rthdr);

	ieee80211_xmit(sdata, NULL, skb);
	rcu_read_unlock();