@@ -1398,7 +1398,8 @@ struct iwl_wowlan_status_data {
u8 key[WOWLAN_KEY_MAX_SIZE];
u8 len;
u8 flags;
- } gtk;
+ u8 id;
+ } gtk[WOWLAN_GTK_KEYS_NUM];
struct {
/*
@@ -1758,7 +1759,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
s8 new_key_id = -1;
if (status->num_of_gtk_rekeys)
- new_key_id = status->gtk.flags &
+ new_key_id = status->gtk[0].flags &
IWL_WOWLAN_GTK_IDX_MASK;
/* Don't install a new key's value to an old key */
@@ -1777,8 +1778,7 @@ 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;
- void *last_gtk;
- u32 cipher;
+ u32 gtk_cipher;
bool unhandled_cipher;
int num_keys;
};
@@ -1804,8 +1804,7 @@ static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw,
case WLAN_CIPHER_SUITE_GCMP_256:
case WLAN_CIPHER_SUITE_TKIP:
/* we support these */
- data->last_gtk = key;
- data->cipher = key->cipher;
+ data->gtk_cipher = key->cipher;
break;
default:
/* everything else - disconnect from AP */
@@ -1824,6 +1823,7 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
{
struct iwl_mvm_d3_gtk_iter_data *data = _data;
struct iwl_wowlan_status_data *status = data->status;
+ s8 keyidx;
if (data->unhandled_cipher)
return;
@@ -1848,14 +1848,76 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
iwl_mvm_set_key_rx_seq_tids(key, status->ptk.tkip.seq);
return;
}
- if (data->status->num_of_gtk_rekeys)
+ keyidx = key->keyidx;
+ /* The current key is always sent by the FW, even if it wasn't
+ * rekeyed during D3.
+ * We remove an existing key if it has the same index as
+ * a new key
+ */
+ if (status->num_of_gtk_rekeys &&
+ ((status->gtk[0].len && keyidx == status->gtk[0].id) ||
+ (status->gtk[1].len && keyidx == status->gtk[1].id))) {
ieee80211_remove_key(key);
-
- if (data->last_gtk == key)
+ } else {
iwl_mvm_set_key_rx_seq(key, data->status, false);
+ }
}
}
+static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
+ struct ieee80211_vif *vif,
+ struct iwl_mvm *mvm, u32 gtk_cipher)
+{
+ int i;
+ struct ieee80211_key_conf *key;
+ struct {
+ struct ieee80211_key_conf conf;
+ u8 key[32];
+ } conf = {
+ .conf.cipher = gtk_cipher,
+ };
+
+ BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256);
+ BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP);
+ BUILD_BUG_ON(sizeof(conf.key) < sizeof(status->gtk[0].key));
+
+ switch (gtk_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_TKIP:
+ conf.conf.keylen = WLAN_KEY_LEN_TKIP;
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(status->gtk); i++) {
+ if (!status->gtk[i].len)
+ continue;
+
+ conf.conf.keyidx = status->gtk[i].id;
+ IWL_DEBUG_WOWLAN(mvm,
+ "Received from FW GTK cipher %d, key index %d\n",
+ conf.conf.cipher, conf.conf.keyidx);
+ memcpy(conf.conf.key, status->gtk[i].key,
+ sizeof(status->gtk[i].key));
+
+ key = ieee80211_gtk_rekey_add(vif, &conf.conf);
+ if (IS_ERR(key))
+ return false;
+ iwl_mvm_set_key_rx_seq_idx(key, status, i);
+ }
+
+ return true;
+}
+
static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_wowlan_status_data *status)
@@ -1883,8 +1945,6 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
return false;
if (!gtkdata.num_keys)
goto out;
- if (!gtkdata.last_gtk)
- return false;
/*
* invalidate all other GTKs that might still exist and update
@@ -1893,52 +1953,14 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
ieee80211_iter_keys(mvm->hw, vif,
iwl_mvm_d3_update_keys, >kdata);
- IWL_DEBUG_WOWLAN(mvm, "num of GTK rekeying %d\n",
- status->num_of_gtk_rekeys);
if (status->num_of_gtk_rekeys) {
- struct ieee80211_key_conf *key;
- struct {
- struct ieee80211_key_conf conf;
- u8 key[32];
- } conf = {
- .conf.cipher = gtkdata.cipher,
- .conf.keyidx =
- status->gtk.flags & IWL_WOWLAN_GTK_IDX_MASK,
- };
- __be64 replay_ctr;
-
- IWL_DEBUG_WOWLAN(mvm,
- "Received from FW GTK cipher %d, key index %d\n",
- conf.conf.cipher, conf.conf.keyidx);
+ __be64 replay_ctr = cpu_to_be64(status->replay_ctr);
- BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
- BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP);
- BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256);
- BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP);
- BUILD_BUG_ON(sizeof(conf.key) < sizeof(status->gtk.key));
+ IWL_DEBUG_WOWLAN(mvm, "num of GTK rekeying %d\n",
+ status->num_of_gtk_rekeys);
- memcpy(conf.conf.key, status->gtk.key, sizeof(status->gtk.key));
-
- switch (gtkdata.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_TKIP:
- conf.conf.keylen = WLAN_KEY_LEN_TKIP;
- break;
- }
-
- key = ieee80211_gtk_rekey_add(vif, &conf.conf);
- if (IS_ERR(key))
+ if (!iwl_mvm_gtk_rekey(status, vif, mvm, gtkdata.gtk_cipher))
return false;
- iwl_mvm_set_key_rx_seq(key, status, true);
-
- replay_ctr = cpu_to_be64(status->replay_ctr);
-
ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
(void *)&replay_ctr, GFP_KERNEL);
}
@@ -1957,40 +1979,52 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
static void iwl_mvm_convert_gtk_v2(struct iwl_wowlan_status_data *status,
struct iwl_wowlan_gtk_status_v2 *data)
{
- BUILD_BUG_ON(sizeof(status->gtk.key) < sizeof(data->key));
+ BUILD_BUG_ON(sizeof(status->gtk[0].key) < sizeof(data->key));
BUILD_BUG_ON(NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY +
sizeof(data->tkip_mic_key) >
- sizeof(status->gtk.key));
+ sizeof(status->gtk[0].key));
- status->gtk.len = data->key_len;
- status->gtk.flags = data->key_flags;
+ status->gtk[0].len = data->key_len;
+ status->gtk[0].flags = data->key_flags;
- memcpy(status->gtk.key, data->key, sizeof(data->key));
+ memcpy(status->gtk[0].key, data->key, sizeof(data->key));
/* if it's as long as the TKIP encryption key, copy MIC key */
- if (status->gtk.len == NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
- memcpy(status->gtk.key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ if (status->gtk[0].len == NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
+ memcpy(status->gtk[0].key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
data->tkip_mic_key, sizeof(data->tkip_mic_key));
}
static void iwl_mvm_convert_gtk_v3(struct iwl_wowlan_status_data *status,
struct iwl_wowlan_gtk_status_v3 *data)
{
- /* The parts we need are identical in v2 and v3 */
-#define CHECK(_f) do { \
- BUILD_BUG_ON(offsetof(struct iwl_wowlan_gtk_status_v2, _f) != \
- offsetof(struct iwl_wowlan_gtk_status_v3, _f)); \
- BUILD_BUG_ON(offsetofend(struct iwl_wowlan_gtk_status_v2, _f) !=\
- offsetofend(struct iwl_wowlan_gtk_status_v3, _f)); \
-} while (0)
+ int data_idx, status_idx = 0;
- CHECK(key);
- CHECK(key_len);
- CHECK(key_flags);
- CHECK(tkip_mic_key);
-#undef CHECK
+ BUILD_BUG_ON(sizeof(status->gtk[0].key) < sizeof(data[0].key));
+ BUILD_BUG_ON(NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY +
+ sizeof(data[0].tkip_mic_key) >
+ sizeof(status->gtk[0].key));
+ BUILD_BUG_ON(ARRAY_SIZE(status->gtk) < WOWLAN_GTK_KEYS_NUM);
+ for (data_idx = 0; data_idx < ARRAY_SIZE(status->gtk); data_idx++) {
+ if (!(data[data_idx].key_len))
+ continue;
+ status->gtk[status_idx].len = data[data_idx].key_len;
+ status->gtk[status_idx].flags = data[data_idx].key_flags;
+ status->gtk[status_idx].id = status->gtk[status_idx].flags &
+ IWL_WOWLAN_GTK_IDX_MASK;
+
+ memcpy(status->gtk[status_idx].key, data[data_idx].key,
+ sizeof(data[data_idx].key));
- iwl_mvm_convert_gtk_v2(status, (void *)data);
+ /* if it's as long as the TKIP encryption key, copy MIC key */
+ if (status->gtk[status_idx].len ==
+ NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
+ memcpy(status->gtk[status_idx].key +
+ NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ data[data_idx].tkip_mic_key,
+ sizeof(data[data_idx].tkip_mic_key));
+ status_idx++;
+ }
}
static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status,
@@ -2033,7 +2067,7 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
}
iwl_mvm_convert_key_counters_v5(status, &data->gtk[0].sc);
- iwl_mvm_convert_gtk_v3(status, &data->gtk[0]);
+ iwl_mvm_convert_gtk_v3(status, data->gtk);
iwl_mvm_convert_igtk(status, &data->igtk[0]);
status->replay_ctr = le64_to_cpu(data->replay_ctr);
@@ -2156,29 +2190,29 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id)
goto out_free_resp;
BUILD_BUG_ON(sizeof(v6->gtk.decrypt_key) >
- sizeof(status->gtk.key));
+ sizeof(status->gtk[0].key));
BUILD_BUG_ON(NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY +
sizeof(v6->gtk.tkip_mic_key) >
- sizeof(status->gtk.key));
+ sizeof(status->gtk[0].key));
/* copy GTK info to the right place */
- memcpy(status->gtk.key, v6->gtk.decrypt_key,
+ memcpy(status->gtk[0].key, v6->gtk.decrypt_key,
sizeof(v6->gtk.decrypt_key));
- memcpy(status->gtk.key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
+ memcpy(status->gtk[0].key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
v6->gtk.tkip_mic_key,
sizeof(v6->gtk.tkip_mic_key));
iwl_mvm_convert_key_counters(status, &v6->gtk.rsc.all_tsc_rsc);
/* hardcode the key length to 16 since v6 only supports 16 */
- status->gtk.len = 16;
+ status->gtk[0].len = 16;
/*
* The key index only uses 2 bits (values 0 to 3) and
* we always set bit 7 which means this is the
* currently used key.
*/
- status->gtk.flags = v6->gtk.key_index | BIT(7);
+ status->gtk[0].flags = v6->gtk.key_index | BIT(7);
} else if (notif_ver == 7) {
struct iwl_wowlan_status_v7 *v7 = (void *)cmd.resp_pkt->data;
@@ -2212,7 +2246,7 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id)
goto out_free_resp;
iwl_mvm_convert_key_counters_v5(status, &v12->gtk[0].sc);
- iwl_mvm_convert_gtk_v3(status, &v12->gtk[0]);
+ iwl_mvm_convert_gtk_v3(status, v12->gtk);
iwl_mvm_convert_igtk(status, &v12->igtk[0]);
status->tid_tear_down = v12->tid_tear_down;