FIX: [mac80211] CVE-2020-3702 ath9k patches

[1/5] ath: Use safer key clearing with key cache entries
[2/5] ath9k: Clear key cache explicitly on disabling hardware
[3/5] ath: Export ath_hw_keysetmac()
[4/5] ath: Modify ath_key_delete() to not need full key entry
[5/5] ath9k: Postpone key cache entry deletion for TXQ frames reference it

BugzId: 74370
This commit is contained in:
Patrick Walther 2021-08-19 18:16:37 +02:00
parent 1d970d96ba
commit 6bf77c5803
1 changed files with 345 additions and 61 deletions

View File

@ -1,35 +1,62 @@
From 6e5ef0edd3782babdeb47e7cc7b67afd3b225d95 Mon Sep 17 00:00:00 2001 From 7ddf76afb4aa795fa6ec60defe149b36abaa9e8b Mon Sep 17 00:00:00 2001
From: Patrick Walther <patrick.walther@netmodule.com> From: Patrick Walther <patrick.walther@netmodule.com>
Date: Mon, 22 Mar 2021 17:52:02 +0100 Date: Mon, 22 Mar 2021 17:52:02 +0100
Subject: [PATCH] 0007 netmodule patches Subject: [PATCH] 0007 netmodule patches
--- ---
drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +++-- drivers/net/wireless/ath/ath.h | 3 +-
drivers/net/wireless/ath/ath10k/mac.c | 40 ++++++++++++++++++++++++------- drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +-
drivers/net/wireless/ath/ath10k/mac.c | 40 ++++++++---
drivers/net/wireless/ath/ath10k/pci.c | 2 +- drivers/net/wireless/ath/ath10k/pci.c | 2 +-
drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +-
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 +-
drivers/net/wireless/ath/ath9k/hw.h | 1 +
drivers/net/wireless/ath/ath9k/main.c | 95 ++++++++++++++++++++++++++-
drivers/net/wireless/ath/key.c | 41 +++++++-----
drivers/net/wireless/ath/regd.c | 2 +- drivers/net/wireless/ath/regd.c | 2 +-
drivers/net/wireless/ath/regd.h | 3 ++- drivers/net/wireless/ath/regd.h | 3 +-
drivers/net/wireless/ath/regd_common.h | 1 + drivers/net/wireless/ath/regd_common.h | 1 +
drivers/net/wireless/ti/wlcore/cmd.c | 7 ------ drivers/net/wireless/ti/wlcore/cmd.c | 7 --
drivers/net/wireless/ti/wlcore/cmd.h | 1 + drivers/net/wireless/ti/wlcore/cmd.h | 1 +
drivers/net/wireless/ti/wlcore/conf.h | 3 +++ drivers/net/wireless/ti/wlcore/conf.h | 3 +
drivers/net/wireless/ti/wlcore/init.c | 22 +++++++++++++---- drivers/net/wireless/ti/wlcore/init.c | 22 +++++--
drivers/net/wireless/ti/wlcore/main.c | 27 +++++++++++++++------ drivers/net/wireless/ti/wlcore/main.c | 28 ++++++--
include/net/cfg80211.h | 11 +++++++-- include/net/cfg80211.h | 11 +++-
include/net/mac80211.h | 8 +++++-- include/net/mac80211.h | 8 ++-
include/uapi/linux/nl80211.h | 3 +++ include/uapi/linux/nl80211.h | 3 +
net/mac80211/cfg.c | 13 ++++++++++ net/mac80211/cfg.c | 13 ++++
net/mac80211/main.c | 4 ++-- net/mac80211/iface.c | 9 ++-
net/wireless/core.c | 41 ++++++++++++++++++++++++++------ net/mac80211/main.c | 4 +-
net/wireless/nl80211.c | 13 ++++++++++ net/wireless/core.c | 41 ++++++++++--
net/wireless/reg.c | 23 ++++++++++++++---- net/wireless/nl80211.c | 13 ++++
19 files changed, 180 insertions(+), 50 deletions(-) net/wireless/reg.c | 23 +++++--
net/wireless/wext-compat.c | 3 +-
27 files changed, 312 insertions(+), 75 deletions(-)
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 2a18e2e..bb30fd7 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -198,12 +198,13 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr);
void ath_hw_setbssidmask(struct ath_common *common);
-void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key);
+void ath_key_delete(struct ath_common *common, u8 hw_key_idx);
int ath_key_config(struct ath_common *common,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);
bool ath_hw_keyreset(struct ath_common *common, u16 entry);
+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac);
void ath_hw_cycle_counters_update(struct ath_common *common);
int32_t ath_hw_get_listen_time(struct ath_common *common);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 5c1af20..5f166bc 100644 index 28ec3c5..9bb791c 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c --- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -3683,8 +3683,10 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data) @@ -3864,8 +3864,10 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
spin_lock_bh(&ar->data_lock); spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, peer_id); peer = ath10k_peer_find_by_id(ar, peer_id);
if (!peer || !peer->sta) { if (!peer || !peer->sta) {
@ -139,6 +166,263 @@ index a7b6b8c..dc8c753 100644
static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO; static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO;
module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644); module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644);
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 47dd580..9b7cbe7 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -516,7 +516,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
break;
case DISABLE_KEY:
- ath_key_delete(common, key);
+ ath_key_delete(common, key->hw_key_idx);
break;
default:
ret = -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index abe6411..0909814 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -1461,7 +1461,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw,
}
break;
case DISABLE_KEY:
- ath_key_delete(common, key);
+ ath_key_delete(common, key->hw_key_idx);
break;
default:
ret = -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 559609d..d152b4b 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -831,6 +831,7 @@ struct ath_hw {
struct ath9k_pacal_info pacal_info;
struct ar5416Stats stats;
struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES];
+ DECLARE_BITMAP(pending_del_keymap, ATH_KEYMAX);
enum ath9k_int imask;
u32 imrs2_reg;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 093eafd..dbd33d7 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -833,12 +833,80 @@ exit:
ieee80211_free_txskb(hw, skb);
}
+static bool ath9k_txq_list_has_key(struct list_head *txq_list, u32 keyix)
+{
+ struct ath_buf *bf;
+ struct ieee80211_tx_info *txinfo;
+ struct ath_frame_info *fi;
+
+ list_for_each_entry(bf, txq_list, list) {
+ if (bf->bf_state.stale || !bf->bf_mpdu)
+ continue;
+
+ txinfo = IEEE80211_SKB_CB(bf->bf_mpdu);
+ fi = (struct ath_frame_info *)&txinfo->rate_driver_data[0];
+ if (fi->keyix == keyix)
+ return true;
+ }
+
+ return false;
+}
+
+static bool ath9k_txq_has_key(struct ath_softc *sc, u32 keyix)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ int i;
+ struct ath_txq *txq;
+ bool key_in_use = false;
+
+ for (i = 0; !key_in_use && i < ATH9K_NUM_TX_QUEUES; i++) {
+ if (!ATH_TXQ_SETUP(sc, i))
+ continue;
+ txq = &sc->tx.txq[i];
+ if (!txq->axq_depth)
+ continue;
+ if (!ath9k_hw_numtxpending(ah, txq->axq_qnum))
+ continue;
+
+ ath_txq_lock(sc, txq);
+ key_in_use = ath9k_txq_list_has_key(&txq->axq_q, keyix);
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
+ int idx = txq->txq_tailidx;
+
+ while (!key_in_use &&
+ !list_empty(&txq->txq_fifo[idx])) {
+ key_in_use = ath9k_txq_list_has_key(
+ &txq->txq_fifo[idx], keyix);
+ INCR(idx, ATH_TXFIFO_DEPTH);
+ }
+ }
+ ath_txq_unlock(sc, txq);
+ }
+
+ return key_in_use;
+}
+
+static void ath9k_pending_key_del(struct ath_softc *sc, u8 keyix)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!test_bit(keyix, ah->pending_del_keymap) ||
+ ath9k_txq_has_key(sc, keyix))
+ return;
+
+ /* No more TXQ frames point to this key cache entry, so delete it. */
+ clear_bit(keyix, ah->pending_del_keymap);
+ ath_key_delete(common, keyix);
+}
+
static void ath9k_stop(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
bool prev_idle;
+ int i;
ath9k_deinit_channel_context(sc);
@@ -906,6 +974,14 @@ static void ath9k_stop(struct ieee80211_hw *hw)
spin_unlock_bh(&sc->sc_pcu_lock);
+ for (i = 0; i < ATH_KEYMAX; i++)
+ ath9k_pending_key_del(sc, i);
+
+ /* Clear key cache entries explicitly to get rid of any potentially
+ * remaining keys.
+ */
+ ath9k_cmn_init_crypto(sc->sc_ah);
+
ath9k_ps_restore(sc);
sc->ps_idle = prev_idle;
@@ -1555,12 +1631,11 @@ static void ath9k_del_ps_key(struct ath_softc *sc,
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_node *an = (struct ath_node *) sta->drv_priv;
- struct ieee80211_key_conf ps_key = { .hw_key_idx = an->ps_key };
if (!an->ps_key)
return;
- ath_key_delete(common, &ps_key);
+ ath_key_delete(common, an->ps_key);
an->ps_key = 0;
an->key_idx[0] = 0;
}
@@ -1731,6 +1806,12 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
if (sta)
an = (struct ath_node *)sta->drv_priv;
+ /* Delete pending key cache entries if no more frames are pointing to
+ * them in TXQs.
+ */
+ for (i = 0; i < ATH_KEYMAX; i++)
+ ath9k_pending_key_del(sc, i);
+
switch (cmd) {
case SET_KEY:
if (sta)
@@ -1760,7 +1841,15 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
}
break;
case DISABLE_KEY:
- ath_key_delete(common, key);
+ if (ath9k_txq_has_key(sc, key->hw_key_idx)) {
+ /* Delay key cache entry deletion until there are no
+ * remaining TXQ frames pointing to this entry.
+ */
+ set_bit(key->hw_key_idx, sc->sc_ah->pending_del_keymap);
+ ath_hw_keysetmac(common, key->hw_key_idx, NULL);
+ } else {
+ ath_key_delete(common, key->hw_key_idx);
+ }
if (an) {
for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
if (an->key_idx[i] != key->hw_key_idx)
diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
index 1816b4e..61b59a8 100644
--- a/drivers/net/wireless/ath/key.c
+++ b/drivers/net/wireless/ath/key.c
@@ -84,8 +84,7 @@ bool ath_hw_keyreset(struct ath_common *common, u16 entry)
}
EXPORT_SYMBOL(ath_hw_keyreset);
-static bool ath_hw_keysetmac(struct ath_common *common,
- u16 entry, const u8 *mac)
+bool ath_hw_keysetmac(struct ath_common *common, u16 entry, const u8 *mac)
{
u32 macHi, macLo;
u32 unicast_flag = AR_KEYTABLE_VALID;
@@ -125,6 +124,7 @@ static bool ath_hw_keysetmac(struct ath_common *common,
return true;
}
+EXPORT_SYMBOL(ath_hw_keysetmac);
static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
const struct ath_keyval *k,
@@ -581,29 +581,38 @@ EXPORT_SYMBOL(ath_key_config);
/*
* Delete Key.
*/
-void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
+void ath_key_delete(struct ath_common *common, u8 hw_key_idx)
{
- ath_hw_keyreset(common, key->hw_key_idx);
- if (key->hw_key_idx < IEEE80211_WEP_NKID)
+ /* Leave CCMP and TKIP (main key) configured to avoid disabling
+ * encryption for potentially pending frames already in a TXQ with the
+ * keyix pointing to this key entry. Instead, only clear the MAC address
+ * to prevent RX processing from using this key cache entry.
+ */
+ if (test_bit(hw_key_idx, common->ccmp_keymap) ||
+ test_bit(hw_key_idx, common->tkip_keymap))
+ ath_hw_keysetmac(common, hw_key_idx, NULL);
+ else
+ ath_hw_keyreset(common, hw_key_idx);
+ if (hw_key_idx < IEEE80211_WEP_NKID)
return;
- clear_bit(key->hw_key_idx, common->keymap);
- clear_bit(key->hw_key_idx, common->ccmp_keymap);
- if (key->cipher != WLAN_CIPHER_SUITE_TKIP)
+ clear_bit(hw_key_idx, common->keymap);
+ clear_bit(hw_key_idx, common->ccmp_keymap);
+ if (!test_bit(hw_key_idx, common->tkip_keymap))
return;
- clear_bit(key->hw_key_idx + 64, common->keymap);
+ clear_bit(hw_key_idx + 64, common->keymap);
- clear_bit(key->hw_key_idx, common->tkip_keymap);
- clear_bit(key->hw_key_idx + 64, common->tkip_keymap);
+ clear_bit(hw_key_idx, common->tkip_keymap);
+ clear_bit(hw_key_idx + 64, common->tkip_keymap);
if (!(common->crypt_caps & ATH_CRYPT_CAP_MIC_COMBINED)) {
- ath_hw_keyreset(common, key->hw_key_idx + 32);
- clear_bit(key->hw_key_idx + 32, common->keymap);
- clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
+ ath_hw_keyreset(common, hw_key_idx + 32);
+ clear_bit(hw_key_idx + 32, common->keymap);
+ clear_bit(hw_key_idx + 64 + 32, common->keymap);
- clear_bit(key->hw_key_idx + 32, common->tkip_keymap);
- clear_bit(key->hw_key_idx + 64 + 32, common->tkip_keymap);
+ clear_bit(hw_key_idx + 32, common->tkip_keymap);
+ clear_bit(hw_key_idx + 64 + 32, common->tkip_keymap);
}
}
EXPORT_SYMBOL(ath_key_delete);
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 0042ea9..f7b15cf 100644 index 0042ea9..f7b15cf 100644
--- a/drivers/net/wireless/ath/regd.c --- a/drivers/net/wireless/ath/regd.c
@ -268,7 +552,7 @@ index 03b49ba..5275d6b 100644
rc.long_retry_limit = 10; rc.long_retry_limit = 10;
rc.aflags = 0; rc.aflags = 0;
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index b5e3a42..5cc837e 100644 index b5e3a42..8fbff25 100644
--- a/drivers/net/wireless/ti/wlcore/main.c --- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2271,13 +2271,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) @@ -2271,13 +2271,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
@ -321,7 +605,7 @@ index b5e3a42..5cc837e 100644
wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
@@ -6227,6 +6238,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) @@ -6227,6 +6239,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_CCMP,
WL1271_CIPHER_SUITE_GEM, WL1271_CIPHER_SUITE_GEM,
@ -329,7 +613,7 @@ index b5e3a42..5cc837e 100644
}; };
/* The tx descriptor buffer */ /* The tx descriptor buffer */
@@ -6290,6 +6302,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) @@ -6290,6 +6303,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
WIPHY_FLAG_IBSS_RSN; WIPHY_FLAG_IBSS_RSN;
wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN;
@ -338,7 +622,7 @@ index b5e3a42..5cc837e 100644
/* make sure all our channels fit in the scanned_ch bitmask */ /* make sure all our channels fit in the scanned_ch bitmask */
BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 449df09..1acf10c 100644 index 092156b..c70a04f 100644
--- a/include/net/cfg80211.h --- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h +++ b/include/net/cfg80211.h
@@ -129,6 +129,7 @@ enum ieee80211_channel_flags { @@ -129,6 +129,7 @@ enum ieee80211_channel_flags {
@ -476,6 +760,40 @@ index 272b433..2a30e87 100644
.set_wds_peer = ieee80211_set_wds_peer, .set_wds_peer = ieee80211_set_wds_peer,
.rfkill_poll = ieee80211_rfkill_poll, .rfkill_poll = ieee80211_rfkill_poll,
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index bf305b2..7998620 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -47,7 +47,7 @@ static void ieee80211_iface_work(struct work_struct *work);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_chanctx_conf *chanctx_conf;
- int power;
+ int power, max_power;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
@@ -56,7 +56,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
return false;
}
- power = ieee80211_chandef_max_power(&chanctx_conf->def);
+ power = max_power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
@@ -65,6 +65,11 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
power = min(power, sdata->ap_power_level);
+ if (sdata->local->user_antenna_gain > 0 && sdata->local->use_chanctx) {
+ max_power -= sdata->local->user_antenna_gain;
+ power = min(power, max_power);
+ }
+
if (power != sdata->vif.bss_conf.txpower) {
sdata->vif.bss_conf.txpower = power;
ieee80211_hw_config(sdata->local, 0);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 44076fb..293bfec 100644 index 44076fb..293bfec 100644
--- a/net/mac80211/main.c --- a/net/mac80211/main.c
@ -663,37 +981,3 @@ index 78f2927..4fa5b0d 100644
data->txpower.flags = IW_TXPOW_DBM; data->txpower.flags = IW_TXPOW_DBM;
return 0; return 0;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index bf305b2..7998620 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -47,7 +47,7 @@ static void ieee80211_iface_work(struct work_struct *work);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_chanctx_conf *chanctx_conf;
- int power;
+ int power, max_power;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
@@ -56,7 +56,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
return false;
}
- power = ieee80211_chandef_max_power(&chanctx_conf->def);
+ power = max_power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
@@ -65,6 +65,11 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
power = min(power, sdata->ap_power_level);
+ if (sdata->local->user_antenna_gain > 0 && sdata->local->use_chanctx) {
+ max_power -= sdata->local->user_antenna_gain;
+ power = min(power, max_power);
+ }
+
if (power != sdata->vif.bss_conf.txpower) {
sdata->vif.bss_conf.txpower = power;
ieee80211_hw_config(sdata->local, 0);