Commit dfdfc2be authored by Sven Eckelmann's avatar Sven Eckelmann Committed by Johannes Berg

mac80211: Parse legacy and HT rate in injected frames

Drivers/devices without their own rate control algorithm can get the
information what rates they should use from either the radiotap header of
injected frames or from the rate control algorithm. But the parsing of the
legacy rate information from the radiotap header was removed in commit
e6a9854b ("mac80211/drivers: rewrite the rate control API").

The removal of this feature heavily reduced the usefulness of frame
injection when wanting to simulate specific transmission behavior. Having
rate parsing together with MCS rates and retry support allows a fine
grained selection of the tx behavior of injected frames for these kind of
tests.
Signed-off-by: default avatarSven Eckelmann <sven@narfation.org>
Cc: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 3f73fe9f
...@@ -28,6 +28,23 @@ radiotap headers and used to control injection: ...@@ -28,6 +28,23 @@ radiotap headers and used to control injection:
IEEE80211_RADIOTAP_F_TX_NOACK: frame should be sent without waiting for IEEE80211_RADIOTAP_F_TX_NOACK: frame should be sent without waiting for
an ACK even if it is a unicast frame an ACK even if it is a unicast frame
* IEEE80211_RADIOTAP_RATE
legacy rate for the transmission (only for devices without own rate control)
* IEEE80211_RADIOTAP_MCS
HT rate for the transmission (only for devices without own rate control).
Also some flags are parsed
IEEE80211_TX_RC_SHORT_GI: use short guard interval
IEEE80211_TX_RC_40_MHZ_WIDTH: send in HT40 mode
* IEEE80211_RADIOTAP_DATA_RETRIES
number of retries when either IEEE80211_RADIOTAP_RATE or
IEEE80211_RADIOTAP_MCS was used
The injection code can also skip all other currently defined radiotap fields The injection code can also skip all other currently defined radiotap fields
facilitating replay of captured radiotap headers directly. facilitating replay of captured radiotap headers directly.
......
...@@ -708,12 +708,14 @@ enum mac80211_tx_info_flags { ...@@ -708,12 +708,14 @@ enum mac80211_tx_info_flags {
* protocol frame (e.g. EAP) * protocol frame (e.g. EAP)
* @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
* frame (PS-Poll or uAPSD). * frame (PS-Poll or uAPSD).
* @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
* *
* These flags are used in tx_info->control.flags. * These flags are used in tx_info->control.flags.
*/ */
enum mac80211_tx_control_flags { enum mac80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0), IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1), IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
}; };
/* /*
......
...@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) ...@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
info->control.short_preamble = txrc.short_preamble; info->control.short_preamble = txrc.short_preamble;
/* don't ask rate control when rate already injected via radiotap */
if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)
return TX_CONTINUE;
if (tx->sta) if (tx->sta)
assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
...@@ -1665,15 +1669,24 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, ...@@ -1665,15 +1669,24 @@ 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 sk_buff *skb) static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
struct sk_buff *skb)
{ {
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;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[info->band];
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
NULL); NULL);
u16 txflags; u16 txflags;
u16 rate = 0;
bool rate_found = false;
u8 rate_retries = 0;
u16 rate_flags = 0;
u8 mcs_known, mcs_flags;
int i;
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_DONTFRAG; IEEE80211_TX_CTL_DONTFRAG;
...@@ -1724,6 +1737,35 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) ...@@ -1724,6 +1737,35 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
info->flags |= IEEE80211_TX_CTL_NO_ACK; info->flags |= IEEE80211_TX_CTL_NO_ACK;
break; break;
case IEEE80211_RADIOTAP_RATE:
rate = *iterator.this_arg;
rate_flags = 0;
rate_found = true;
break;
case IEEE80211_RADIOTAP_DATA_RETRIES:
rate_retries = *iterator.this_arg;
break;
case IEEE80211_RADIOTAP_MCS:
mcs_known = iterator.this_arg[0];
mcs_flags = iterator.this_arg[1];
if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS))
break;
rate_found = true;
rate = iterator.this_arg[2];
rate_flags = IEEE80211_TX_RC_MCS;
if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI &&
mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
rate_flags |= IEEE80211_TX_RC_SHORT_GI;
if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
break;
/* /*
* Please update the file * Please update the file
* Documentation/networking/mac80211-injection.txt * Documentation/networking/mac80211-injection.txt
...@@ -1738,6 +1780,32 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) ...@@ -1738,6 +1780,32 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
return false; return false;
if (rate_found) {
info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
info->control.rates[i].idx = -1;
info->control.rates[i].flags = 0;
info->control.rates[i].count = 0;
}
if (rate_flags & IEEE80211_TX_RC_MCS) {
info->control.rates[0].idx = rate;
} else {
for (i = 0; i < sband->n_bitrates; i++) {
if (rate * 5 != sband->bitrates[i].bitrate)
continue;
info->control.rates[0].idx = i;
break;
}
}
info->control.rates[0].flags = rate_flags;
info->control.rates[0].count = min_t(u8, rate_retries + 1,
local->hw.max_rate_tries);
}
/* /*
* remove the radiotap header * remove the radiotap header
* iterator->_max_length was sanity-checked against * iterator->_max_length was sanity-checked against
...@@ -1819,7 +1887,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, ...@@ -1819,7 +1887,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
IEEE80211_TX_CTL_INJECTED; IEEE80211_TX_CTL_INJECTED;
/* process and remove the injection radiotap header */ /* process and remove the injection radiotap header */
if (!ieee80211_parse_tx_radiotap(skb)) if (!ieee80211_parse_tx_radiotap(local, skb))
goto fail; goto fail;
rcu_read_lock(); rcu_read_lock();
......
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