forked from Minki/linux
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: 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>
This commit is contained in:
parent
08aca29aa8
commit
cb17ed29a7
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user