@@ -279,7 +279,6 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta,
{
struct iwl_mvm_sta *mvmsta;
struct iwl_mvm_vif *mvmvif;
- u8 fwkeyid = u32_get_bits(status, IWL_RX_MPDU_STATUS_KEY);
u8 keyid;
struct ieee80211_key_conf *key;
u32 len = le16_to_cpu(desc->mpdu_len);
@@ -299,6 +298,10 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta,
if (!ieee80211_is_beacon(hdr->frame_control))
return 0;
+ /* key mismatch - will also report !MIC_OK but we shouldn't count it */
+ if (!(status & IWL_RX_MPDU_STATUS_KEY_VALID))
+ return -1;
+
/* good cases */
if (likely(status & IWL_RX_MPDU_STATUS_MIC_OK &&
!(status & IWL_RX_MPDU_STATUS_REPLAY_ERROR)))
@@ -309,26 +312,36 @@ static int iwl_mvm_rx_mgmt_prot(struct ieee80211_sta *sta,
mvmsta = iwl_mvm_sta_from_mac80211(sta);
- /* what? */
- if (fwkeyid != 6 && fwkeyid != 7)
- return -1;
-
mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
- key = rcu_dereference(mvmvif->bcn_prot.keys[fwkeyid - 6]);
- if (!key)
- return -1;
+ /*
+ * both keys will have the same cipher and MIC length, use
+ * whichever one is available
+ */
+ key = rcu_dereference(mvmvif->bcn_prot.keys[0]);
+ if (!key) {
+ key = rcu_dereference(mvmvif->bcn_prot.keys[1]);
+ if (!key)
+ return -1;
+ }
if (len < key->icv_len + IEEE80211_GMAC_PN_LEN + 2)
return -1;
- /*
- * See if the key ID matches - if not this may be due to a
- * switch and the firmware may erroneously report !MIC_OK.
- */
+ /* get the real key ID */
keyid = frame[len - key->icv_len - IEEE80211_GMAC_PN_LEN - 2];
- if (keyid != fwkeyid)
- return -1;
+ /* and if that's the other key, look it up */
+ if (keyid != key->keyidx) {
+ /*
+ * shouldn't happen since firmware checked, but be safe
+ * in case the MIC length is wrong too, for example
+ */
+ if (keyid != 6 && keyid != 7)
+ return -1;
+ key = rcu_dereference(mvmvif->bcn_prot.keys[keyid - 6]);
+ if (!key)
+ return -1;
+ }
/* Report status to mac80211 */
if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))