diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index ef98efc8fb1b..c44f02f225ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "mld.h" @@ -951,19 +951,25 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, iwl_mld_update_mcast_rx_seq(key_config, key_data); - /* The FW holds only one igtk so we keep track of the valid one */ + /* The FW holds only one IGTK so we keep track of the valid one */ if (key_config->keyidx == 4 || key_config->keyidx == 5) { - struct iwl_mld_link *mld_link = - iwl_mld_link_from_mac80211(link_conf); + struct iwl_mld_link_sta *mld_ap_link_sta; + + mld_ap_link_sta = iwl_mld_get_ap_link_sta(vif, + link_conf->link_id); + if (WARN_ON(!mld_ap_link_sta)) + return; /* If we had more than one rekey, mac80211 will tell us to * remove the old and add the new so we will update the IGTK in * drv_set_key */ - if (mld_link->igtk && mld_link->igtk != key_config) { + if (mld_ap_link_sta->rx_igtk && + mld_ap_link_sta->rx_igtk != key_config) { /* mark the old IGTK as not in FW */ - mld_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID; - mld_link->igtk = key_config; + mld_ap_link_sta->rx_igtk->hw_key_idx = + STA_KEY_IDX_INVALID; + mld_ap_link_sta->rx_igtk = key_config; } } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h index ce4f8ca885cf..147ff6787ed9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -289,4 +289,19 @@ static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif) struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld); +static inline struct iwl_mld_link_sta * +iwl_mld_get_ap_link_sta(struct ieee80211_vif *vif, int link_id) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_sta *ap_sta = mld_vif->ap_sta; + struct iwl_mld_sta *mld_ap_sta; + + if (!ap_sta) + return NULL; + + mld_ap_sta = iwl_mld_sta_from_mac80211(ap_sta); + + return iwl_mld_link_sta_dereference_check(mld_ap_sta, link_id); +} + #endif /* __iwl_mld_iface_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c index 2cb312ebd7a7..944d5487c110 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/key.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -170,34 +170,69 @@ static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask, iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd); } +static struct ieee80211_key_conf ** +iwl_mld_get_igtk_ptr(struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + /* key's link ID is set to -1 for non-MLO */ + int link_id = key->link_id < 0 ? 0 : key->link_id; + struct iwl_mld_link_sta *mld_ap_link_sta; + struct iwl_mld_link *mld_link; + struct iwl_mld_sta *mld_sta; + + if (key->keyidx != 4 && key->keyidx != 5) + return NULL; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (WARN_ON(!sta)) + return NULL; + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_ap_link_sta = iwl_mld_link_sta_dereference_check(mld_sta, + link_id); + if (WARN_ON(!mld_ap_link_sta)) + return NULL; + + return &mld_ap_link_sta->rx_igtk; + case NL80211_IFTYPE_NAN: + if (sta) { + mld_sta = iwl_mld_sta_from_mac80211(sta); + + return &mld_sta->deflink.rx_igtk; + } + + return &mld_vif->deflink.tx_igtk; + case NL80211_IFTYPE_AP: + mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); + if (WARN_ON(!mld_link)) + return NULL; + + return &mld_link->tx_igtk; + default: + WARN_ONCE(1, "invalid iftype %d for IGTK\n", vif->type); + return NULL; + } +} + void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_key_conf **igtk_ptr; lockdep_assert_wiphy(mld->wiphy); if (!sta_mask) return; - if (key->keyidx == 4 || key->keyidx == 5) { - struct iwl_mld_link *mld_link; - unsigned int link_id = 0; - - /* set to -1 for non-MLO right now */ - if (key->link_id >= 0) - link_id = key->link_id; - - mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); - if (WARN_ON(!mld_link)) - return; - - if (mld_link->igtk == key) - mld_link->igtk = NULL; - + igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key); + if (igtk_ptr && *igtk_ptr == key) { + *igtk_ptr = NULL; mld->num_igtks--; } @@ -214,9 +249,7 @@ int iwl_mld_add_key(struct iwl_mld *mld, { u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); - struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); - struct iwl_mld_link *mld_link = NULL; - bool igtk = key->keyidx == 4 || key->keyidx == 5; + struct ieee80211_key_conf **igtk_ptr; int ret; lockdep_assert_wiphy(mld->wiphy); @@ -224,36 +257,25 @@ int iwl_mld_add_key(struct iwl_mld *mld, if (!sta_mask) return -EINVAL; - if (igtk) { + igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key); + if (igtk_ptr) { if (mld->num_igtks == IWL_MAX_NUM_IGTKS) return -EOPNOTSUPP; - u8 link_id = 0; - - /* set to -1 for non-MLO right now */ - if (key->link_id >= 0) - link_id = key->link_id; - - mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); - - if (WARN_ON(!mld_link)) - return -EINVAL; - - if (mld_link->igtk) { + if (*igtk_ptr) { IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n", - mld_link->igtk->keyidx); - iwl_mld_remove_key(mld, vif, sta, mld_link->igtk); + (*igtk_ptr)->keyidx); + iwl_mld_remove_key(mld, vif, sta, *igtk_ptr); } - - WARN_ON(mld_link->igtk); } ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key); if (ret) return ret; - if (mld_link) { - mld_link->igtk = key; + if (igtk_ptr) { + WARN_ON(*igtk_ptr); + *igtk_ptr = key; mld->num_igtks++; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index 84d9a24134a8..2b3e6b55367f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_link_h__ #define __iwl_mld_link_h__ @@ -34,8 +34,9 @@ struct iwl_probe_resp_data { * @chan_ctx: pointer to the channel context assigned to the link. If a link * has an assigned channel context it means that it is active. * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked. - * @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two. - * This tracks the one IGTK that currently exists in FW. + * @tx_igtk: FW can only have one IGTK per MAC at a time, whereas mac80211 can + * have two. This tracks the one IGTK that currently exists in FW, for TX + * purposes. The RX IGTKs are tracked per station. * @bigtks: BIGTKs of the AP. Only valid for STA mode. * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS. * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS. @@ -60,7 +61,7 @@ struct iwl_mld_link { struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; struct ieee80211_chanctx_conf __rcu *chan_ctx; bool he_ru_2mhz_block; - struct ieee80211_key_conf *igtk; + struct ieee80211_key_conf *tx_igtk; struct ieee80211_key_conf __rcu *bigtks[2]; ); /* And here fields that survive a fw restart */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h index 13644ffd185d..dff14ff0a5af 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -28,6 +28,9 @@ struct iwl_mld_rxq_dup_data { * This represents the link-level sta - the driver level equivalent to the * ieee80211_link_sta * + * @rx_igtk: FW can only have one IGTK for RX at a time, whereas mac80211 will + * have two. This tracks the one IGTK that currently exists in FW, to + * remove it there when a new one is installed. * @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif * @signal_avg: the signal average coming from the firmware * @in_fw: whether the link STA is uploaded to the FW (false during restart) @@ -37,6 +40,7 @@ struct iwl_mld_rxq_dup_data { struct iwl_mld_link_sta { /* Add here fields that need clean up on restart */ struct_group(zeroed_on_hw_restart, + struct ieee80211_key_conf *rx_igtk; u32 last_rate_n_flags; bool in_fw; s8 signal_avg;