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
e6a9854b05c1 ("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: Sven Eckelmann <[email protected]>
Cc: Simon Wunderlich <[email protected]>
---
v2:
- resent RFC as v2 because first patch was not correctly marked as RFC
- removed check from ieee80211_xmit_fast because this one is never called
for injected frames
Documentation/networking/mac80211-injection.txt | 17 ++++++
include/net/mac80211.h | 2 +
net/mac80211/tx.c | 73 ++++++++++++++++++++++++-
3 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/Documentation/networking/mac80211-injection.txt b/Documentation/networking/mac80211-injection.txt
index 3a93007..ec8f934 100644
--- a/Documentation/networking/mac80211-injection.txt
+++ b/Documentation/networking/mac80211-injection.txt
@@ -28,6 +28,23 @@ radiotap headers and used to control injection:
IEEE80211_RADIOTAP_F_TX_NOACK: frame should be sent without waiting for
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
facilitating replay of captured radiotap headers directly.
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 566df20..3c3c1f3 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -708,12 +708,14 @@ enum mac80211_tx_info_flags {
* protocol frame (e.g. EAP)
* @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
* 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.
*/
enum mac80211_tx_control_flags {
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
+ IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
};
/*
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3311ce0..4c79f47 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1476,7 +1476,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
CALL_TXH(ieee80211_tx_h_ps_buf);
CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
CALL_TXH(ieee80211_tx_h_select_key);
- if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
+ if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL) &&
+ !(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT))
CALL_TXH(ieee80211_tx_h_rate_ctrl);
if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
@@ -1665,15 +1666,24 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
ieee80211_tx(sdata, sta, skb, false);
}
-static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
+static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
+ struct ieee80211_local *local)
{
struct ieee80211_radiotap_iterator iterator;
struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data;
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,
NULL);
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 |
IEEE80211_TX_CTL_DONTFRAG;
@@ -1724,6 +1734,35 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
info->flags |= IEEE80211_TX_CTL_NO_ACK;
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
* Documentation/networking/mac80211-injection.txt
@@ -1738,6 +1777,34 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
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;
+ if (rate_retries + 1 > local->hw.max_rate_tries)
+ info->control.rates[0].count = local->hw.max_rate_tries;
+ else
+ info->control.rates[0].count = rate_retries + 1;
+ }
+
/*
* remove the radiotap header
* iterator->_max_length was sanity-checked against
@@ -1819,7 +1886,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
IEEE80211_TX_CTL_INJECTED;
/* process and remove the injection radiotap header */
- if (!ieee80211_parse_tx_radiotap(skb))
+ if (!ieee80211_parse_tx_radiotap(skb, local))
goto fail;
rcu_read_lock();
Hi,
> CALL_TXH(ieee80211_tx_h_select_key);
> - if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
> + if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL) &&
> + !(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT))
> CALL_TXH(ieee80211_tx_h_rate_ctrl);
This seems a bit problematic. ieee80211_tx_h_rate_ctrl() also sets up
some protection, e.g.
info->control.use_rts = txrc.rts;
info->control.use_cts_prot = tx->sdata->vif.bss_conf.use_cts_prot;
and preamble settings
info->control.short_preamble = txrc.short_preamble;
Are you sure you want to skip that entirely?
> + info->control.rates[0].flags = rate_flags;
> + if (rate_retries + 1 > local->hw.max_rate_tries)
> + info->control.rates[0].count = local-
> >hw.max_rate_tries;
> + else
> + info->control.rates[0].count = rate_retries
> + 1;
max() or max_t()?
> /* process and remove the injection radiotap header */
> - if (!ieee80211_parse_tx_radiotap(skb))
> + if (!ieee80211_parse_tx_radiotap(skb, local))
> goto fail;
I'd prefer adding "local" as the first argument since that's far more
commonly done in mac80211.
johannes
On Tuesday 26 January 2016 14:27:20 Johannes Berg wrote:
> Hi,
> > CALL_TXH(ieee80211_tx_h_select_key);
> > - if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
> > + if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL) &&
> > + !(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT))
> > CALL_TXH(ieee80211_tx_h_rate_ctrl);
>
> This seems a bit problematic. ieee80211_tx_h_rate_ctrl() also sets up
> some protection, e.g.
>
> info->control.use_rts = txrc.rts;
> info->control.use_cts_prot = tx->sdata->vif.bss_conf.use_cts_prot;
>
> and preamble settings
>
> info->control.short_preamble = txrc.short_preamble;
>
> Are you sure you want to skip that entirely?
You are correct, these should be set but rate_control_get_rate should be
skipped.
>
> > + info->control.rates[0].flags = rate_flags;
> > + if (rate_retries + 1 > local->hw.max_rate_tries)
> > + info->control.rates[0].count = local-
> > >hw.max_rate_tries;
> > + else
> > + info->control.rates[0].count = rate_retries
> > + 1;
>
> max() or max_t()?
min_t - but yes, you are right.
Btw. we can also change it when you prefer that by default injected frames
with rates but without DATA_RETRIES should get local->hw.max_rate_tries as
retries.
>
> > /* process and remove the injection radiotap header */
> > - if (!ieee80211_parse_tx_radiotap(skb))
> > + if (!ieee80211_parse_tx_radiotap(skb, local))
> > goto fail;
>
> I'd prefer adding "local" as the first argument since that's far more
> commonly done in mac80211.
Ok, will be changed.
Kind regards,
Sven