Skip to content

Commit

Permalink
mac80211: parse radiotap header when selecting Tx queue
Browse files Browse the repository at this point in the history
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: Mathy Vanhoef <Mathy.Vanhoef@kuleuven.be>
Link: https://lore.kernel.org/r/20200723100153.31631-6-Mathy.Vanhoef@kuleuven.be
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
vanhoefm authored and jmberg-intel committed Jul 31, 2020
1 parent 08aca29 commit cb17ed2
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 34 deletions.
8 changes: 8 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
15 changes: 11 additions & 4 deletions net/mac80211/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
54 changes: 24 additions & 30 deletions net/mac80211/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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;
}

Expand All @@ -2204,30 +2210,24 @@ 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;
struct cfg80211_chan_def *chandef;
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
Expand Down Expand Up @@ -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();

/*
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit cb17ed2

Please sign in to comment.