diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 67e0df14ba0f..002aa31b559b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2195,7 +2195,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * enabling hardware crypto failed. The set_key() call may also return the * value 1 to permit this specific key/algorithm to be done in software. * - * When the cmd is %DISABLE_KEY then it must succeed. + * When the cmd is %DISABLE_KEY then it must succeed. If RX uses software + * PN checking in mac80211, the function must ensure that after returning + * no frame decrypted with this key can be handed to mac80211's RX, i.e. + * it must do the necessary tasklet/interrupt thread synchronisation. * * Note that it is permissible to not decrypt a frame even if a key * for it has been uploaded to hardware, the stack will not make any diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 2e677376c958..53e83704079e 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -599,6 +599,25 @@ int ieee80211_key_link(struct ieee80211_key *key, increment_tailroom_need_count(sdata); + /* In the case of PTK rekeying without "Extended Key ID for + * Individually Addressed Frames" capability, the key index 0 will + * be reused as the spec then mandates using key index 0 for PTKs. + * In this case, we must not let the following happen: + * - old key is replaced by new key here + * - HW-decrypted (with old key) frame is RX-processed, and given + * the key matching it bumps up the PN for the *new* key + * - [key gets HW acceleration, but that is now no longer relevant] + * - practically all future frames are dropped as the new key uses + * a small PN and all frames are detected as 'replays' + * + * Avoid this by removing the old key from HW acceleration *before* + * linking in the new key. + */ + if (local->ops->set_key && old_key) { + /* XXX - should flush our local->tasklet here...? */ + ieee80211_key_disable_hw_accel(old_key); + } + ieee80211_key_replace(sdata, sta, pairwise, old_key, key); ieee80211_key_destroy(old_key, true);