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

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.beSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 08aca29a
...@@ -6238,6 +6238,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, ...@@ -6238,6 +6238,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct sk_buff *skb, struct ieee80211_vif *vif, struct sk_buff *skb,
int band, struct ieee80211_sta **sta); 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 * struct ieee80211_noa_data - holds temporary data for tracking P2P NoA state
* *
......
...@@ -1183,17 +1183,24 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, ...@@ -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_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
struct ieee80211_radiotap_header *rtap = (void *)skb->data; int len_rthdr;
if (local->hw.queues < IEEE80211_NUM_ACS) if (local->hw.queues < IEEE80211_NUM_ACS)
return 0; return 0;
if (skb->len < 4 || /* reset flags and info before parsing radiotap header */
skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */) memset(info, 0, sizeof(*info));
if (!ieee80211_parse_tx_radiotap(skb, dev))
return 0; /* doesn't matter, frame will be dropped */ 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); return ieee80211_select_queue_80211(sdata, skb, hdr);
} }
......
...@@ -2015,9 +2015,10 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, ...@@ -2015,9 +2015,10 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
ieee80211_tx(sdata, sta, skb, false); ieee80211_tx(sdata, sta, skb, false);
} }
static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local, bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
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_iterator iterator;
struct ieee80211_radiotap_header *rthdr = struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data; (struct ieee80211_radiotap_header *) skb->data;
...@@ -2036,6 +2037,18 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local, ...@@ -2036,6 +2037,18 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
u8 vht_mcs = 0, vht_nss = 0; u8 vht_mcs = 0, vht_nss = 0;
int i; 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 | info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_DONTFRAG; IEEE80211_TX_CTL_DONTFRAG;
...@@ -2189,13 +2202,6 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local, ...@@ -2189,13 +2202,6 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
local->hw.max_rate_tries); 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; return true;
} }
...@@ -2204,8 +2210,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, ...@@ -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_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_chanctx_conf *chanctx_conf; 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_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
struct ieee80211_sub_if_data *tmp_sdata, *sdata; struct ieee80211_sub_if_data *tmp_sdata, *sdata;
...@@ -2213,21 +2217,17 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, ...@@ -2213,21 +2217,17 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
u16 len_rthdr; u16 len_rthdr;
int hdrlen; int hdrlen;
/* check for not even having the fixed radiotap header part */ memset(info, 0, sizeof(*info));
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
goto fail; /* too short to be possibly valid */ IEEE80211_TX_CTL_INJECTED;
/* is it a header version we can trust to find length from? */ /* Sanity-check and process the injection radiotap header */
if (unlikely(prthdr->it_version)) if (!ieee80211_parse_tx_radiotap(skb, dev))
goto fail; /* only version 0 is supported */ 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); 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 * fix up the pointers accounting for the radiotap
* header still being in there. We are being given * header still being in there. We are being given
...@@ -2273,11 +2273,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, ...@@ -2273,11 +2273,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; 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(); rcu_read_lock();
/* /*
...@@ -2343,9 +2338,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, ...@@ -2343,9 +2338,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
info->band = chandef->chan->band; info->band = chandef->chan->band;
/* process and remove the injection radiotap header */ /* remove the injection radiotap header */
if (!ieee80211_parse_tx_radiotap(local, skb)) skb_pull(skb, len_rthdr);
goto fail_rcu;
ieee80211_xmit(sdata, NULL, skb); ieee80211_xmit(sdata, NULL, skb);
rcu_read_unlock(); rcu_read_unlock();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment