Commit d90ab6e3 authored by Shaul Triebitz's avatar Shaul Triebitz Committed by Johannes Berg

wifi: iwlwifi: mvm: support wowlan notif version 4

In version 4, in case of MLO GTK rekey during D3,
the firmware sends all the new keys, including
the keys on the non-active links.

Update also the non active link keys.
Signed-off-by: default avatarShaul Triebitz <shaul.triebitz@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240311081938.6524de988ed3.Id065ddd2f4a71b0243c33ae0c5476ac41bfe2dc2@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a26fe2d0
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/* /*
* Copyright (C) 2012-2014, 2018-2023 Intel Corporation * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH
*/ */
...@@ -843,6 +843,52 @@ struct iwl_wowlan_info_notif_v2 { ...@@ -843,6 +843,52 @@ struct iwl_wowlan_info_notif_v2 {
u8 reserved2[2]; u8 reserved2[2];
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_2 */ } __packed; /* WOWLAN_INFO_NTFY_API_S_VER_2 */
/* MAX MLO keys of non-active links that can arrive in the notification */
#define WOWLAN_MAX_MLO_KEYS 18
/**
* enum iwl_wowlan_mlo_gtk_type - GTK types
* @WOWLAN_MLO_GTK_KEY_TYPE_GTK: GTK
* @WOWLAN_MLO_GTK_KEY_TYPE_IGTK: IGTK
* @WOWLAN_MLO_GTK_KEY_TYPE_BIGTK: BIGTK
* @WOWLAN_MLO_GTK_KEY_NUM_TYPES: number of key types
*/
enum iwl_wowlan_mlo_gtk_type {
WOWLAN_MLO_GTK_KEY_TYPE_GTK,
WOWLAN_MLO_GTK_KEY_TYPE_IGTK,
WOWLAN_MLO_GTK_KEY_TYPE_BIGTK,
WOWLAN_MLO_GTK_KEY_NUM_TYPES
}; /* WOWLAN_MLO_GTK_KEY_TYPE_API_E_VER_1 */
/**
* enum iwl_wowlan_mlo_gtk_flag - MLO GTK flags
* @WOWLAN_MLO_GTK_FLAG_KEY_LEN_MSK: 0 for len 16, 1 for len 32
* @WOWLAN_MLO_GTK_FLAG_KEY_ID_MSK: key id (ranges from 0 to 7)
* @WOWLAN_MLO_GTK_FLAG_LINK_ID_MSK: spec link id of the key
* @WOWLAN_MLO_GTK_FLAG_KEY_TYPE_MSK: &enum iwl_wowlan_mlo_gtk_type
* @WOWLAN_MLO_GTK_FLAG_LAST_KEY_MSK: is this the last given key per
* key-type / link-id - the currently used key
*/
enum iwl_wowlan_mlo_gtk_flag {
WOWLAN_MLO_GTK_FLAG_KEY_LEN_MSK = 0x0001,
WOWLAN_MLO_GTK_FLAG_KEY_ID_MSK = 0x000E,
WOWLAN_MLO_GTK_FLAG_LINK_ID_MSK = 0x00F0,
WOWLAN_MLO_GTK_FLAG_KEY_TYPE_MSK = 0x0300,
WOWLAN_MLO_GTK_FLAG_LAST_KEY_MSK = 0x0400
}; /* WOWLAN_MLO_GTK_FLAG_API_E_VER_1 */
/**
* struct iwl_wowlan_mlo_gtk - MLO GTK info
* @key: key material
* @flags: &enum iwl_wowlan_mlo_gtk_flag
* @pn: packet number
*/
struct iwl_wowlan_mlo_gtk {
u8 key[WOWLAN_KEY_MAX_SIZE];
__le16 flags;
u8 pn[6];
} __packed; /* WOWLAN_MLO_GTK_KEY_API_S_VER_1 */
/** /**
* struct iwl_wowlan_info_notif - WoWLAN information notification * struct iwl_wowlan_info_notif - WoWLAN information notification
* @gtk: GTK data * @gtk: GTK data
...@@ -859,7 +905,10 @@ struct iwl_wowlan_info_notif_v2 { ...@@ -859,7 +905,10 @@ struct iwl_wowlan_info_notif_v2 {
* @tid_tear_down: bit mask of tids whose BA sessions were closed * @tid_tear_down: bit mask of tids whose BA sessions were closed
* in suspend state * in suspend state
* @station_id: station id * @station_id: station id
* @num_mlo_link_keys: number of &struct iwl_wowlan_mlo_gtk structs
* following this notif, or reserved in version < 4
* @reserved2: reserved * @reserved2: reserved
* @mlo_gtks: array of GTKs of size num_mlo_link_keys for version >= 4
*/ */
struct iwl_wowlan_info_notif { struct iwl_wowlan_info_notif {
struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM]; struct iwl_wowlan_gtk_status_v3 gtk[WOWLAN_GTK_KEYS_NUM];
...@@ -875,8 +924,10 @@ struct iwl_wowlan_info_notif { ...@@ -875,8 +924,10 @@ struct iwl_wowlan_info_notif {
__le32 received_beacons; __le32 received_beacons;
u8 tid_tear_down; u8 tid_tear_down;
u8 station_id; u8 station_id;
u8 reserved2[2]; u8 num_mlo_link_keys;
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_3 */ u8 reserved2;
struct iwl_wowlan_mlo_gtk mlo_gtks[];
} __packed; /* WOWLAN_INFO_NTFY_API_S_VER_3, _VER_4 */
/** /**
* struct iwl_wowlan_wake_pkt_notif - WoWLAN wake packet notification * struct iwl_wowlan_wake_pkt_notif - WoWLAN wake packet notification
......
...@@ -1471,6 +1471,9 @@ struct iwl_wowlan_status_data { ...@@ -1471,6 +1471,9 @@ struct iwl_wowlan_status_data {
struct iwl_multicast_key_data igtk; struct iwl_multicast_key_data igtk;
struct iwl_multicast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM]; struct iwl_multicast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM];
int num_mlo_keys;
struct iwl_wowlan_mlo_gtk mlo_keys[WOWLAN_MAX_MLO_KEYS];
u8 *wake_packet; u8 *wake_packet;
}; };
...@@ -1980,6 +1983,169 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, ...@@ -1980,6 +1983,169 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
} }
} }
struct iwl_mvm_d3_mlo_old_keys {
u32 cipher[IEEE80211_MLD_MAX_NUM_LINKS][WOWLAN_MLO_GTK_KEY_NUM_TYPES];
struct ieee80211_key_conf *key[IEEE80211_MLD_MAX_NUM_LINKS][8];
};
static void iwl_mvm_mlo_key_ciphers(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key,
void *data)
{
struct iwl_mvm_d3_mlo_old_keys *old_keys = data;
enum iwl_wowlan_mlo_gtk_type key_type;
if (key->link_id < 0)
return;
if (WARN_ON(key->link_id >= IEEE80211_MLD_MAX_NUM_LINKS ||
key->keyidx >= 8))
return;
if (WARN_ON(old_keys->key[key->link_id][key->keyidx]))
return;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
key_type = WOWLAN_MLO_GTK_KEY_TYPE_GTK;
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) {
key_type = WOWLAN_MLO_GTK_KEY_TYPE_IGTK;
break;
} else if (key->keyidx == 6 || key->keyidx == 7) {
key_type = WOWLAN_MLO_GTK_KEY_TYPE_BIGTK;
break;
}
return;
default:
/* ignore WEP/TKIP or unknown ciphers */
return;
}
old_keys->cipher[key->link_id][key_type] = key->cipher;
old_keys->key[key->link_id][key->keyidx] = key;
}
static bool iwl_mvm_mlo_gtk_rekey(struct iwl_wowlan_status_data *status,
struct ieee80211_vif *vif,
struct iwl_mvm *mvm)
{
int i;
struct iwl_mvm_d3_mlo_old_keys *old_keys;
bool ret = true;
IWL_DEBUG_WOWLAN(mvm, "Num of MLO Keys: %d\n", status->num_mlo_keys);
if (!status->num_mlo_keys)
return true;
old_keys = kzalloc(sizeof(*old_keys), GFP_KERNEL);
if (!old_keys)
return false;
/* find the cipher for each mlo key */
ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mlo_key_ciphers, old_keys);
for (i = 0; i < status->num_mlo_keys; i++) {
struct iwl_wowlan_mlo_gtk *mlo_key = &status->mlo_keys[i];
struct ieee80211_key_conf *key, *old_key;
struct ieee80211_key_seq seq;
struct {
struct ieee80211_key_conf conf;
u8 key[32];
} conf = {};
u16 flags = le16_to_cpu(mlo_key->flags);
int j, link_id, key_id, key_type;
link_id = u16_get_bits(flags, WOWLAN_MLO_GTK_FLAG_LINK_ID_MSK);
key_id = u16_get_bits(flags, WOWLAN_MLO_GTK_FLAG_KEY_ID_MSK);
key_type = u16_get_bits(flags,
WOWLAN_MLO_GTK_FLAG_KEY_TYPE_MSK);
if (!(vif->valid_links & BIT(link_id)))
continue;
if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS ||
key_id >= 8 ||
key_type >= WOWLAN_MLO_GTK_KEY_NUM_TYPES))
continue;
conf.conf.cipher = old_keys->cipher[link_id][key_type];
/* WARN_ON? */
if (!conf.conf.cipher)
continue;
conf.conf.keylen = 0;
switch (conf.conf.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_GCMP:
conf.conf.keylen = WLAN_KEY_LEN_CCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
break;
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;
}
if (WARN_ON(!conf.conf.keylen ||
conf.conf.keylen > sizeof(conf.key)))
continue;
memcpy(conf.conf.key, mlo_key->key, conf.conf.keylen);
conf.conf.keyidx = key_id;
old_key = old_keys->key[link_id][key_id];
if (old_key) {
IWL_DEBUG_WOWLAN(mvm,
"Remove MLO key id %d, link id %d\n",
key_id, link_id);
ieee80211_remove_key(old_key);
}
IWL_DEBUG_WOWLAN(mvm, "Add MLO key id %d, link id %d\n",
key_id, link_id);
key = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id);
if (WARN_ON(IS_ERR(key))) {
ret = false;
goto out;
}
/*
* mac80211 expects the pn in big-endian
* also note that seq is a union of all cipher types
* (ccmp, gcmp, cmac, gmac), and they all have the same
* pn field (of length 6) so just copy it to ccmp.pn.
*/
for (j = 5; j >= 0; j--)
seq.ccmp.pn[5 - j] = mlo_key->pn[j];
/* group keys are non-QoS and use TID 0 */
ieee80211_set_key_rx_seq(key, 0, &seq);
}
out:
kfree(old_keys);
return ret;
}
static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status, static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct iwl_mvm *mvm, u32 gtk_cipher) struct iwl_mvm *mvm, u32 gtk_cipher)
...@@ -2183,6 +2349,9 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, ...@@ -2183,6 +2349,9 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
return false; return false;
} }
if (!iwl_mvm_mlo_gtk_rekey(status, vif, mvm))
return false;
ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
(void *)&replay_ctr, GFP_KERNEL); (void *)&replay_ctr, GFP_KERNEL);
} }
...@@ -2310,9 +2479,10 @@ static void iwl_mvm_convert_bigtk(struct iwl_wowlan_status_data *status, ...@@ -2310,9 +2479,10 @@ static void iwl_mvm_convert_bigtk(struct iwl_wowlan_status_data *status,
static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
struct iwl_wowlan_info_notif *data, struct iwl_wowlan_info_notif *data,
struct iwl_wowlan_status_data *status, struct iwl_wowlan_status_data *status,
u32 len) u32 len, bool has_mlo_keys)
{ {
u32 i; u32 i;
u32 expected_len = sizeof(*data);
if (!data) { if (!data) {
IWL_ERR(mvm, "iwl_wowlan_info_notif data is NULL\n"); IWL_ERR(mvm, "iwl_wowlan_info_notif data is NULL\n");
...@@ -2320,7 +2490,11 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, ...@@ -2320,7 +2490,11 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
return; return;
} }
if (len < sizeof(*data)) { if (has_mlo_keys)
expected_len += (data->num_mlo_link_keys *
sizeof(status->mlo_keys[0]));
if (len < expected_len) {
IWL_ERR(mvm, "Invalid WoWLAN info notification!\n"); IWL_ERR(mvm, "Invalid WoWLAN info notification!\n");
status = NULL; status = NULL;
return; return;
...@@ -2340,6 +2514,17 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm, ...@@ -2340,6 +2514,17 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
le32_to_cpu(data->num_of_gtk_rekeys); le32_to_cpu(data->num_of_gtk_rekeys);
status->received_beacons = le32_to_cpu(data->received_beacons); status->received_beacons = le32_to_cpu(data->received_beacons);
status->tid_tear_down = data->tid_tear_down; status->tid_tear_down = data->tid_tear_down;
if (has_mlo_keys && data->num_mlo_link_keys) {
status->num_mlo_keys = data->num_mlo_link_keys;
if (IWL_FW_CHECK(mvm,
status->num_mlo_keys > WOWLAN_MAX_MLO_KEYS,
"Too many mlo keys: %d, max %d\n",
status->num_mlo_keys, WOWLAN_MAX_MLO_KEYS))
status->num_mlo_keys = WOWLAN_MAX_MLO_KEYS;
memcpy(status->mlo_keys, data->mlo_gtks,
status->num_mlo_keys * sizeof(status->mlo_keys[0]));
}
} }
static void static void
...@@ -3086,7 +3271,8 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, ...@@ -3086,7 +3271,8 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait,
(void *)pkt->data; (void *)pkt->data;
iwl_mvm_parse_wowlan_info_notif(mvm, notif, iwl_mvm_parse_wowlan_info_notif(mvm, notif,
d3_data->status, len); d3_data->status, len,
wowlan_info_ver > 3);
} }
d3_data->notif_received |= IWL_D3_NOTIF_WOWLAN_INFO; d3_data->notif_received |= IWL_D3_NOTIF_WOWLAN_INFO;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment