diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h
index 09d3a945c5d9..2be7ff54fc40 100644
--- a/drivers/net/ovpn/crypto.h
+++ b/drivers/net/ovpn/crypto.h
@@ -43,6 +43,8 @@ struct ovpn_crypto_key_slot {
 
 	struct crypto_aead *encrypt;
 	struct crypto_aead *decrypt;
+	atomic64_t decrypt_failures;
+	unsigned long decrypt_failure_flags;
 	u8 nonce_tail_xmit[OVPN_NONCE_TAIL_SIZE];
 	u8 nonce_tail_recv[OVPN_NONCE_TAIL_SIZE];
 
diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c
index 9b10ae6f6002..9e34a553f59a 100644
--- a/drivers/net/ovpn/crypto_aead.c
+++ b/drivers/net/ovpn/crypto_aead.c
@@ -29,6 +29,25 @@
 
 #define ALG_NAME_AES		"gcm(aes)"
 #define ALG_NAME_CHACHAPOLY	"rfc7539(chacha20,poly1305)"
+#define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY	BIT_ULL(35)
+#define OVPN_AEAD_DECRYPT_FAILURE_LIMIT		BIT_ULL(36)
+#define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT	0
+
+static bool
+ovpn_aead_decrypt_failure_exceeded(const struct ovpn_crypto_key_slot *ks)
+{
+	return atomic64_read(&ks->decrypt_failures) >
+	       OVPN_AEAD_DECRYPT_FAILURE_LIMIT;
+}
+
+bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks)
+{
+	u64 failures = atomic64_inc_return(&ks->decrypt_failures);
+
+	return failures > OVPN_AEAD_DECRYPT_FAILURE_NOTIFY &&
+	       !test_and_set_bit(OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT,
+				 &ks->decrypt_failure_flags);
+}
 
 static int ovpn_aead_encap_overhead(const struct ovpn_crypto_key_slot *ks)
 {
@@ -270,6 +289,9 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
 	if (unlikely(payload_len < 0))
 		return -EINVAL;
 
+	if (unlikely(ovpn_aead_decrypt_failure_exceeded(ks)))
+		return -EKEYREJECTED;
+
 	/* Prepare the skb data buffer to be accessed up until the auth tag.
 	 * This is required because this area is directly mapped into the sg
 	 * list.
@@ -436,6 +458,8 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc)
 	ovpn_key_usage_limit_init(&ks->usage_limit, kc->cipher_alg);
 	ovpn_key_usage_init(&ks->usage_xmit);
 	ovpn_key_usage_init(&ks->usage_recv);
+	atomic64_set(&ks->decrypt_failures, 0);
+	ks->decrypt_failure_flags = 0;
 
 	ks->encrypt = ovpn_aead_init("encrypt", alg_name,
 				     kc->encrypt.cipher_key,
diff --git a/drivers/net/ovpn/crypto_aead.h b/drivers/net/ovpn/crypto_aead.h
index 65a2ff307898..e5ff0b9b5ee3 100644
--- a/drivers/net/ovpn/crypto_aead.h
+++ b/drivers/net/ovpn/crypto_aead.h
@@ -25,5 +25,6 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc);
 void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks);
 
 enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks);
+bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks);
 
 #endif /* _NET_OVPN_OVPNAEAD_H_ */
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index 31c750b9481a..8085cc345c59 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -129,6 +129,12 @@ void ovpn_decrypt_post(void *data, int ret)
 	/* crypto is done, cleanup skb CB and its members */
 	kfree(ovpn_skb_cb(skb)->crypto_tmp);
 
+	if (unlikely(ret == -EBADMSG)) {
+		if (unlikely(ovpn_aead_decrypt_failure_record(ks)))
+			ovpn_nl_key_swap_notify(peer, ks->key_id);
+		goto drop;
+	}
+
 	if (unlikely(ret < 0))
 		goto drop;
 
