--- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -90,6 +90,7 @@ struct ieee80211_key { * Management frames. */ u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6]; + u8 rx_invalid_pn[NUM_RX_DATA_QUEUES + 1]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ #ifndef AES_BLOCK_LEN --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -457,6 +457,13 @@ ieee80211_crypto_ccmp_encrypt(struct iee return TX_CONTINUE; } +static inline u64 pn_to_u64(u8 *data) +{ + u64 pn = get_unaligned_be32(data + 2); + pn |= ((u64) get_unaligned_be16(data)) << 32; + return pn; +} + ieee80211_rx_result ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) @@ -469,6 +476,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee u8 pn[CCMP_PN_LEN]; int data_len; int queue; + u64 diff; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -503,6 +511,11 @@ ieee80211_crypto_ccmp_decrypt(struct iee return RX_DROP_UNUSABLE; } + diff = pn_to_u64(pn) - pn_to_u64(key->u.ccmp.rx_pn[queue]); + if (diff > 1000 && key->u.ccmp.rx_invalid_pn[queue]++ < 10) + return RX_DROP_UNUSABLE; + + key->u.ccmp.rx_invalid_pn[queue] = 0; memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN); /* Remove CCMP header and MIC */