diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/api/d3.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 168 |
2 files changed, 147 insertions, 25 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index 380eeb2363c7..72d461c47323 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -396,6 +396,7 @@ struct iwl_wowlan_config_cmd { #define WOWLAN_KEY_MAX_SIZE 32 #define WOWLAN_GTK_KEYS_NUM 2 #define WOWLAN_IGTK_KEYS_NUM 2 +#define WOWLAN_IGTK_MIN_INDEX 4 /* * WOWLAN_TSC_RSC_PARAMS @@ -612,6 +613,7 @@ struct iwl_wowlan_gtk_status_v3 { } __packed; /* WOWLAN_GTK_MATERIAL_VER_3 */ #define IWL_WOWLAN_GTK_IDX_MASK (BIT(0) | BIT(1)) +#define IWL_WOWLAN_IGTK_BIGTK_IDX_MASK (BIT(0)) /** * struct iwl_wowlan_igtk_status - IGTK status diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 1d900342224f..f6488b4bbe68 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1380,6 +1380,14 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return __iwl_mvm_suspend(hw, wowlan, false); } +struct iwl_multicast_key_data { + u8 key[WOWLAN_KEY_MAX_SIZE]; + u8 len; + u8 flags; + u8 id; + u8 ipn[6]; +}; + /* converted data from the different status responses */ struct iwl_wowlan_status_data { u64 replay_ctr; @@ -1429,12 +1437,7 @@ struct iwl_wowlan_status_data { } tkip, aes; } ptk; - struct { - u64 ipn; - u8 key[WOWLAN_KEY_MAX_SIZE]; - u8 len; - u8 flags; - } igtk; + struct iwl_multicast_key_data igtk; u8 *wake_packet; }; @@ -1778,8 +1781,8 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, struct iwl_mvm_d3_gtk_iter_data { struct iwl_mvm *mvm; struct iwl_wowlan_status_data *status; - u32 gtk_cipher; - bool unhandled_cipher; + u32 gtk_cipher, igtk_cipher; + bool unhandled_cipher, igtk_support; int num_keys; }; @@ -1806,6 +1809,19 @@ static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw, /* we support these */ data->gtk_cipher = key->cipher; break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + /* we support these */ + if (data->igtk_support && + (key->keyidx == 4 || key->keyidx == 5)) { + data->igtk_cipher = key->cipher; + } else { + data->unhandled_cipher = true; + return; + } + break; default: /* everything else - disconnect from AP */ data->unhandled_cipher = true; @@ -1815,6 +1831,23 @@ static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw, data->num_keys++; } +static void +iwl_mvm_d3_set_igtk_bigtk_ipn(const struct iwl_multicast_key_data *key, + struct ieee80211_key_seq *seq, u32 cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + BUILD_BUG_ON(sizeof(seq->aes_gmac.pn) != sizeof(key->ipn)); + memcpy(seq->aes_gmac.pn, key->ipn, sizeof(seq->aes_gmac.pn)); + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + BUILD_BUG_ON(sizeof(seq->aes_cmac.pn) != sizeof(key->ipn)); + memcpy(seq->aes_cmac.pn, key->ipn, sizeof(seq->aes_cmac.pn)); + break; + } +} + static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1861,6 +1894,24 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, } else { iwl_mvm_set_key_rx_seq(key, data->status, false); } + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (key->keyidx == 4 || key->keyidx == 5) { + /* remove rekeyed key */ + if (status->num_of_gtk_rekeys) { + ieee80211_remove_key(key); + } else { + struct ieee80211_key_seq seq; + + iwl_mvm_d3_set_igtk_bigtk_ipn(&status->igtk, + &seq, + key->cipher); + ieee80211_set_key_rx_seq(key, 0, &seq); + } + } } } @@ -1918,6 +1969,70 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status, return true; } +static bool +iwl_mvm_d3_igtk_bigtk_rekey_add(struct iwl_wowlan_status_data *status, + struct ieee80211_vif *vif, u32 cipher, + struct iwl_multicast_key_data *key_data) +{ + struct ieee80211_key_conf *key_config; + struct { + struct ieee80211_key_conf conf; + u8 key[WOWLAN_KEY_MAX_SIZE]; + } conf = { + .conf.cipher = cipher, + .conf.keyidx = key_data->id, + }; + struct ieee80211_key_seq seq; + + if (!key_data->len) + return true; + + iwl_mvm_d3_set_igtk_bigtk_ipn(key_data, &seq, conf.conf.cipher); + + switch (cipher) { + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256; + break; + default: + WARN_ON(1); + } + BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key)); + memcpy(conf.conf.key, key_data->key, conf.conf.keylen); + + key_config = ieee80211_gtk_rekey_add(vif, &conf.conf); + if (IS_ERR(key_config)) + return false; + ieee80211_set_key_rx_seq(key_config, 0, &seq); + return true; +} + +static int iwl_mvm_lookup_wowlan_status_ver(struct iwl_mvm *mvm) +{ + u8 notif_ver; + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL)) + return 6; + + /* default to 7 (when we have IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL) */ + notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, + WOWLAN_GET_STATUSES, 0); + if (!notif_ver) + notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + WOWLAN_GET_STATUSES, 7); + + return notif_ver; +} + static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_wowlan_status_data *status) @@ -1937,6 +2052,12 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, if (status->wakeup_reasons & disconnection_reasons) return false; + if (iwl_mvm_lookup_wowlan_status_ver(mvm) > 6 || + iwl_fw_lookup_notif_ver(mvm->fw, PROT_OFFLOAD_GROUP, + WOWLAN_INFO_NOTIFICATION, + 0)) + gtkdata.igtk_support = true; + /* find last GTK that we used initially, if any */ ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_d3_find_last_keys, >kdata); @@ -1961,6 +2082,12 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, if (!iwl_mvm_gtk_rekey(status, vif, mvm, gtkdata.gtk_cipher)) return false; + + if (!iwl_mvm_d3_igtk_bigtk_rekey_add(status, vif, + gtkdata.igtk_cipher, + &status->igtk)) + return false; + ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, (void *)&replay_ctr, GFP_KERNEL); } @@ -2030,21 +2157,19 @@ static void iwl_mvm_convert_gtk_v3(struct iwl_wowlan_status_data *status, static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status, struct iwl_wowlan_igtk_status *data) { - const u8 *ipn = data->ipn; - BUILD_BUG_ON(sizeof(status->igtk.key) < sizeof(data->key)); + if (!data->key_len) + return; + status->igtk.len = data->key_len; status->igtk.flags = data->key_flags; + status->igtk.id = u32_get_bits(data->key_flags, + IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) + + WOWLAN_IGTK_MIN_INDEX; memcpy(status->igtk.key, data->key, sizeof(data->key)); - - status->igtk.ipn = ((u64)ipn[5] << 0) | - ((u64)ipn[4] << 8) | - ((u64)ipn[3] << 16) | - ((u64)ipn[2] << 24) | - ((u64)ipn[1] << 32) | - ((u64)ipn[0] << 40); + memcpy(status->igtk.ipn, data->ipn, sizeof(data->ipn)); } static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, @@ -2175,14 +2300,9 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id) len = iwl_rx_packet_payload_len(cmd.resp_pkt); /* default to 7 (when we have IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL) */ - notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, - WOWLAN_GET_STATUSES, 0); - if (!notif_ver) - notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, - WOWLAN_GET_STATUSES, 7); + notif_ver = iwl_mvm_lookup_wowlan_status_ver(mvm); - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL)) { + if (notif_ver < 7) { struct iwl_wowlan_status_v6 *v6 = (void *)cmd.resp_pkt->data; status = iwl_mvm_parse_wowlan_status_common_v6(mvm, v6, len); |