diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c | 264 |
1 files changed, 223 insertions, 41 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c index e27c893502f7..ef0be44207e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2022 - 2024 Intel Corporation */ #include <linux/kernel.h> #include <net/mac80211.h> @@ -14,34 +14,61 @@ static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm, struct ieee80211_key_conf *keyconf) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif_link_info *link_info = &mvmvif->deflink; - if (vif->type == NL80211_IFTYPE_AP && - !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - return BIT(mvmvif->mcast_sta.sta_id); + lockdep_assert_held(&mvm->mutex); - if (sta) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (keyconf->link_id >= 0) { + link_info = mvmvif->link[keyconf->link_id]; + if (!link_info) + return 0; + } - return BIT(mvmsta->sta_id); + /* AP group keys are per link and should be on the mcast/bcast STA */ + if (vif->type == NL80211_IFTYPE_AP && + !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* IGTK/BIGTK to bcast STA */ + if (keyconf->keyidx >= 4) + return BIT(link_info->bcast_sta.sta_id); + /* GTK for data to mcast STA */ + return BIT(link_info->mcast_sta.sta_id); } - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) - return BIT(mvmvif->ap_sta_id); + /* for client mode use the AP STA also for group keys */ + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mvmvif->ap_sta; + + /* During remove the STA was removed and the group keys come later + * (which sounds like a bad sequence, but remember that to mac80211 the + * group keys have no sta pointer), so we don't have a STA now. + * Since this happens for group keys only, just use the link_info as + * the group keys are per link; make sure that is the case by checking + * we do have a link_id or are not doing MLO. + * Of course the same can be done during add as well, but we must do + * it during remove, since we don't have the mvmvif->ap_sta pointer. + */ + if (!sta && (keyconf->link_id >= 0 || !ieee80211_vif_is_mld(vif))) + return BIT(link_info->ap_sta_id); - /* invalid */ - return 0; + /* STA should be non-NULL now, but iwl_mvm_sta_fw_id_mask() checks */ + + /* pass link_id to filter by it if not -1 (GTK on client) */ + return iwl_mvm_sta_fw_id_mask(mvm, sta, keyconf->link_id); } -static u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf) +u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool pairwise = keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE; + bool igtk = keyconf->keyidx == 4 || keyconf->keyidx == 5; u32 flags = 0; - if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + lockdep_assert_held(&mvm->mutex); + + if (!pairwise) flags |= IWL_SEC_KEY_FLAG_MCAST_KEY; switch (keyconf->cipher) { @@ -68,22 +95,78 @@ static u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm, break; } - rcu_read_lock(); - if (!sta && vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { - u8 sta_id = mvmvif->ap_sta_id; + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mvmvif->ap_sta; - sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - } - - if (!IS_ERR_OR_NULL(sta) && sta->mfp) + /* + * If we are installing an iGTK (in AP or STA mode), we need to tell + * the firmware this key will en/decrypt MGMT frames. + * Same goes if we are installing a pairwise key for an MFP station. + * In case we're installing a groupwise key (which is not an iGTK), + * then, we will not use this key for MGMT frames. + */ + if ((!IS_ERR_OR_NULL(sta) && sta->mfp && pairwise) || igtk) flags |= IWL_SEC_KEY_FLAG_MFP; - rcu_read_unlock(); + + if (keyconf->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) + flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU; return flags; } +struct iwl_mvm_sta_key_update_data { + struct ieee80211_sta *sta; + u32 old_sta_mask; + u32 new_sta_mask; + int err; +}; + +static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); + struct iwl_mvm_sta_key_update_data *data = _data; + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY), + .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask), + .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask), + .u.modify.key_id = cpu_to_le32(key->keyidx), + .u.modify.key_flags = + cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)), + }; + int err; + + /* only need to do this for pairwise keys (link_id == -1) */ + if (sta != data->sta || key->link_id >= 0) + return; + + err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + + if (err) + data->err = err; +} + +int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 old_sta_mask, + u32 new_sta_mask) +{ + struct iwl_mvm_sta_key_update_data data = { + .sta = sta, + .old_sta_mask = old_sta_mask, + .new_sta_mask = new_sta_mask, + }; + + ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key, + &data); + return data.err; +} + static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags, u32 keyidx, u32 flags) { @@ -98,13 +181,9 @@ static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask, return iwl_mvm_send_cmd_pdu(mvm, cmd_id, flags, sizeof(cmd), &cmd); } -int iwl_mvm_sec_key_add(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf) +int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags, + struct ieee80211_key_conf *keyconf) { - u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf); - u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf); u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); struct iwl_sec_key_cmd cmd = { .action = cpu_to_le32(FW_CTXT_ACTION_ADD), @@ -113,9 +192,17 @@ int iwl_mvm_sec_key_add(struct iwl_mvm *mvm, .u.add.key_flags = cpu_to_le32(key_flags), .u.add.tx_seq = cpu_to_le64(atomic64_read(&keyconf->tx_pn)), }; + int max_key_len = sizeof(cmd.u.add.key); int ret; - if (WARN_ON(keyconf->keylen > sizeof(cmd.u.add.key))) + if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) + max_key_len -= IWL_SEC_WEP_KEY_OFFSET; + + if (WARN_ON(keyconf->keylen > max_key_len)) + return -EINVAL; + + if (WARN_ON(!sta_mask)) return -EINVAL; if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || @@ -154,6 +241,58 @@ int iwl_mvm_sec_key_add(struct iwl_mvm *mvm, return ret; } +int iwl_mvm_sec_key_add(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) +{ + u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf); + u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif_link_info *mvm_link = NULL; + int ret; + + if (keyconf->keyidx == 4 || keyconf->keyidx == 5) { + unsigned int link_id = 0; + + /* set to -1 for non-MLO right now */ + if (keyconf->link_id >= 0) + link_id = keyconf->link_id; + + mvm_link = mvmvif->link[link_id]; + if (WARN_ON(!mvm_link)) + return -EINVAL; + + if (mvm_link->igtk) { + IWL_DEBUG_MAC80211(mvm, "remove old IGTK %d\n", + mvm_link->igtk->keyidx); + ret = iwl_mvm_sec_key_del(mvm, vif, sta, + mvm_link->igtk); + if (ret) + IWL_ERR(mvm, + "failed to remove old IGTK (ret=%d)\n", + ret); + } + + WARN_ON(mvm_link->igtk); + } + + ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf); + if (ret) + return ret; + + if (mvm_link) + mvm_link->igtk = keyconf; + + /* We don't really need this, but need it to be not invalid, + * and if we switch links multiple times it might go to be + * invalid when removed. + */ + keyconf->hw_key_idx = 0; + + return 0; +} + static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -162,8 +301,31 @@ static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm, { u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf); u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; + if (WARN_ON(!sta_mask)) + return -EINVAL; + + if (keyconf->keyidx == 4 || keyconf->keyidx == 5) { + struct iwl_mvm_vif_link_info *mvm_link; + unsigned int link_id = 0; + + /* set to -1 for non-MLO right now */ + if (keyconf->link_id >= 0) + link_id = keyconf->link_id; + + mvm_link = mvmvif->link[link_id]; + if (WARN_ON(!mvm_link)) + return -EINVAL; + + if (mvm_link->igtk == keyconf) { + /* no longer in HW - mark for later */ + mvm_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID; + mvm_link->igtk = NULL; + } + } + ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx, flags); if (ret) @@ -180,6 +342,21 @@ static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm, return ret; } +int iwl_mvm_sec_key_del_pasn(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 sta_mask, + struct ieee80211_key_conf *keyconf) +{ + u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) | + IWL_SEC_KEY_FLAG_MFP; + + if (WARN_ON(!sta_mask)) + return -EINVAL; + + return __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx, + 0); +} + int iwl_mvm_sec_key_del(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -195,6 +372,7 @@ static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw, void *data) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + unsigned int link_id = (uintptr_t)data; if (key->hw_key_idx == STA_KEY_IDX_INVALID) return; @@ -202,25 +380,29 @@ static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw, if (sta) return; + if (key->link_id >= 0 && key->link_id != link_id) + return; + _iwl_mvm_sec_key_del(mvm, vif, NULL, key, CMD_ASYNC); key->hw_key_idx = STA_KEY_IDX_INVALID; } void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + struct iwl_mvm_vif_link_info *link, + unsigned int link_id) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD); u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0); - if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || - mvmvif->ap_sta_id == IWL_MVM_INVALID_STA)) + if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION || + link->ap_sta_id == IWL_INVALID_STA)) return; if (!sec_key_ver) return; - ieee80211_iter_keys_rcu(mvm->hw, vif, - iwl_mvm_sec_key_remove_ap_iter, - NULL); + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_sec_key_remove_ap_iter, + (void *)(uintptr_t)link_id); } |
