mac80211_hwsim: check TX and STA bandwidth

Add checks to hwsim to validate that neither TX nor any
station's configured bandwidth can exceed the channel
(context) configuration previously requested.

Link: https://lore.kernel.org/r/20220214173004.9fd154d2c3c2.Ia0cd152357a373149bab017d479ab7d5ded289c0@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2022-02-14 17:29:48 +01:00
parent a3a20feb32
commit 585625c955

View File

@ -654,6 +654,7 @@ struct mac80211_hwsim_data {
ARRAY_SIZE(hwsim_channels_6ghz)];
struct ieee80211_channel *channel;
enum nl80211_chan_width bw;
u64 beacon_int /* beacon interval in us */;
unsigned int rx_filter;
bool started, idle, scanning;
@ -808,6 +809,38 @@ extern int hwsim_tx_virtio(struct mac80211_hwsim_data *data,
#define hwsim_virtio_enabled false
#endif
static int hwsim_get_chanwidth(enum nl80211_chan_width bw)
{
switch (bw) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
return 20;
case NL80211_CHAN_WIDTH_40:
return 40;
case NL80211_CHAN_WIDTH_80:
return 80;
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
return 160;
case NL80211_CHAN_WIDTH_5:
return 5;
case NL80211_CHAN_WIDTH_10:
return 10;
case NL80211_CHAN_WIDTH_1:
return 1;
case NL80211_CHAN_WIDTH_2:
return 2;
case NL80211_CHAN_WIDTH_4:
return 4;
case NL80211_CHAN_WIDTH_8:
return 8;
case NL80211_CHAN_WIDTH_16:
return 16;
}
return INT_MAX;
}
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct sk_buff *skb,
struct ieee80211_channel *chan);
@ -1623,7 +1656,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *channel;
bool ack;
u32 _portid;
enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT;
u32 _portid, i;
if (WARN_ON(skb->len < 10)) {
/* Should not happen; just a sanity check for addr1 use */
@ -1633,14 +1667,17 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
if (!data->use_chanctx) {
channel = data->channel;
confbw = data->bw;
} else if (txi->hw_queue == 4) {
channel = data->tmp_chan;
} else {
chanctx_conf = rcu_dereference(txi->control.vif->chanctx_conf);
if (chanctx_conf)
if (chanctx_conf) {
channel = chanctx_conf->def.chan;
else
confbw = chanctx_conf->def.width;
} else {
channel = NULL;
}
}
if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
@ -1664,6 +1701,25 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
txi->control.rates,
ARRAY_SIZE(txi->control.rates));
for (i = 0; i < ARRAY_SIZE(txi->control.rates); i++) {
u16 rflags = txi->control.rates[i].flags;
/* initialize to data->bw for 5/10 MHz handling */
enum nl80211_chan_width bw = data->bw;
if (txi->control.rates[i].idx == -1)
break;
if (rflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
bw = NL80211_CHAN_WIDTH_40;
else if (rflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
bw = NL80211_CHAN_WIDTH_80;
else if (rflags & IEEE80211_TX_RC_160_MHZ_WIDTH)
bw = NL80211_CHAN_WIDTH_160;
if (WARN_ON(hwsim_get_chanwidth(bw) > hwsim_get_chanwidth(confbw)))
return;
}
if (skb->len >= 24 + 8 &&
ieee80211_is_probe_resp(hdr->frame_control)) {
/* fake header transmission time */
@ -1963,6 +2019,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
}
data->channel = conf->chandef.chan;
data->bw = conf->chandef.width;
for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) {
if (data->survey_data[idx].channel &&
@ -1974,6 +2031,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
}
} else {
data->channel = conf->chandef.chan;
data->bw = conf->chandef.width;
}
mutex_unlock(&data->mutex);
@ -2105,12 +2163,48 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
wiphy_dbg(hw->wiphy, " TX Power: %d dBm\n", info->txpower);
}
static void
mac80211_hwsim_sta_rc_update(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u32 changed)
{
struct mac80211_hwsim_data *data = hw->priv;
u32 bw = U32_MAX;
enum nl80211_chan_width confbw = NL80211_CHAN_WIDTH_20_NOHT;
switch (sta->bandwidth) {
#define C(_bw) case IEEE80211_STA_RX_BW_##_bw: bw = _bw; break
C(20);
C(40);
C(80);
C(160);
#undef C
}
if (!data->use_chanctx) {
confbw = data->bw;
} else {
struct ieee80211_chanctx_conf *chanctx_conf =
rcu_dereference(vif->chanctx_conf);
if (!WARN_ON(!chanctx_conf))
confbw = chanctx_conf->def.width;
}
WARN(bw > hwsim_get_chanwidth(confbw),
"intf %pM: bad STA %pM bandwidth %d MHz (%d) > channel config %d MHz (%d)\n",
vif->addr, sta->addr, bw, sta->bandwidth,
hwsim_get_chanwidth(data->bw), data->bw);
}
static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
hwsim_check_magic(vif);
hwsim_set_sta_magic(sta);
mac80211_hwsim_sta_rc_update(hw, vif, sta, 0);
return 0;
}
@ -2677,6 +2771,7 @@ static int mac80211_hwsim_tx_last_beacon(struct ieee80211_hw *hw)
.sta_add = mac80211_hwsim_sta_add, \
.sta_remove = mac80211_hwsim_sta_remove, \
.sta_notify = mac80211_hwsim_sta_notify, \
.sta_rc_update = mac80211_hwsim_sta_rc_update, \
.set_tim = mac80211_hwsim_set_tim, \
.conf_tx = mac80211_hwsim_conf_tx, \
.get_survey = mac80211_hwsim_get_survey, \