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..db1be8f9ea06 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -599,9 +599,35 @@ int ieee80211_key_link(struct ieee80211_key *key, increment_tailroom_need_count(sdata); + /* In the case of PTK rekeying, the key index 0 will be reused as + * the spec mandates using key index 0 for PTKs. In this case, it + * can happen that the following happens: + * - 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' + * + * To try to avoid this problem, set the key to drop packets here + * as soon as it becomes visible to the RX path -- here we set it + * before the key is activated in ieee80211_key_replace(). + * + * After the old key was removed from HW acceleration (which we thus + * assume to be synchronous) drop this flag. This will fix the PN + * problem at the expense of dropping some old and new frames. + */ + if (local->ops->set_key && old_key) + key->flags |= KEY_FLAG_DROP_WHILE_UPLOADING; + ieee80211_key_replace(sdata, sta, pairwise, old_key, key); ieee80211_key_destroy(old_key, true); + if (local->ops->set_key && old_key) { + /* XXX - should somehow flush our local->tasklet here... */ + key->flags &= ~KEY_FLAG_DROP_WHILE_UPLOADING; + } + ieee80211_debugfs_key_add(key); if (!local->wowlan) { diff --git a/net/mac80211/key.h b/net/mac80211/key.h index df430a618764..244cb5baf75d 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -29,12 +29,15 @@ struct sta_info; * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present * in the hardware for TX crypto hardware acceleration. * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped. + * @KEY_FLAG_DROP_WHILE_UPLOADING: frames that appear to be for this key + * should not be processed in RX until the key upload is complete * @KEY_FLAG_CIPHER_SCHEME: This key is for a hardware cipher scheme */ enum ieee80211_internal_key_flags { KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), KEY_FLAG_TAINTED = BIT(1), - KEY_FLAG_CIPHER_SCHEME = BIT(2), + KEY_FLAG_DROP_WHILE_UPLOADING = BIT(2), + KEY_FLAG_CIPHER_SCHEME = BIT(3), }; enum ieee80211_internal_tkip_state { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index aa35977a9c4d..dc4409cc5aaf 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1644,7 +1644,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) } if (rx->key) { - if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) + if (unlikely(rx->key->flags & (KEY_FLAG_TAINTED | + KEY_FLAG_DROP_WHILE_UPLOADING))) return RX_DROP_MONITOR; rx->key->tx_rx_count++;